diff options
-rw-r--r-- | table/destination.go | 835 | ||||
-rw-r--r-- | table/destination_test.go | 207 | ||||
-rw-r--r-- | table/path.go | 339 | ||||
-rw-r--r-- | table/path_test.go | 270 | ||||
-rw-r--r-- | table/table.go | 252 | ||||
-rw-r--r-- | table/table_manager.go | 424 | ||||
-rw-r--r-- | table/table_manager_test.go | 1482 | ||||
-rw-r--r-- | table/temporary_structs.go | 34 | ||||
-rw-r--r-- | utils/collection.go | 199 | ||||
-rw-r--r-- | utils/collection_test.go | 162 |
10 files changed, 4204 insertions, 0 deletions
diff --git a/table/destination.go b/table/destination.go new file mode 100644 index 00000000..7857c0fc --- /dev/null +++ b/table/destination.go @@ -0,0 +1,835 @@ +// 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 table + +import ( + "encoding/binary" + "fmt" + "github.com/osrg/gobgp/packet" + "net" + "reflect" +) + +const ( + BPR_UNKNOWN = "Unknown" + BPR_ONLY_PATH = "Only Path" + BPR_REACHABLE_NEXT_HOP = "Reachable Next Hop" + BPR_HIGHEST_WEIGHT = "Highest Weight" + BPR_LOCAL_PREF = "Local Pref" + BPR_LOCAL_ORIGIN = "Local Origin" + BPR_ASPATH = "AS Path" + BPR_ORIGIN = "Origin" + BPR_MED = "MED" + BPR_ASN = "ASN" + BPR_IGP_COST = "IGP Cost" + BPR_ROUTER_ID = "Router ID" +) + +type Destination interface { + Calculate(localAsn uint32) (Path, string, error) + getRouteFamily() RouteFamily + setRouteFamily(ROUTE_FAMILY RouteFamily) + getNlri() bgp.AddrPrefixInterface + setNlri(nlri bgp.AddrPrefixInterface) + getBestPathReason() string + setBestPathReason(string) + getBestPath() Path + setBestPath(path Path) + getKnownPathList() []Path + String() string + addWithdraw(withdraw Path) + addNewPath(newPath Path) + addSentRoute(sentRoute *SentRoute) + removeSentRoute(peer *Peer) bool + constructWithdrawPath() Path + removeOldPathsFromSource(source *Peer) []Path +} + +type DestinationDefault struct { + ROUTE_FAMILY RouteFamily + coreService CoreService + nlri bgp.AddrPrefixInterface + knownPathList []Path + withdrawList []Path + newPathList []Path + bestPath Path + bestPathReason string + sentRoutes map[*Peer]*SentRoute +} + +func NewDestinationDefault(nlri bgp.AddrPrefixInterface) *DestinationDefault { + destination := &DestinationDefault{} + destination.ROUTE_FAMILY = RF_IPv4_UC + destination.nlri = nlri + destination.knownPathList = make([]Path, 0) + destination.withdrawList = make([]Path, 0) + destination.newPathList = make([]Path, 0) + destination.bestPath = nil + destination.bestPathReason = "" + destination.sentRoutes = make(map[*Peer]*SentRoute) + return destination +} + +func (dd *DestinationDefault) getRouteFamily() RouteFamily { + return dd.ROUTE_FAMILY +} + +func (dd *DestinationDefault) setRouteFamily(ROUTE_FAMILY RouteFamily) { + dd.ROUTE_FAMILY = ROUTE_FAMILY +} + +func (dd *DestinationDefault) getNlri() bgp.AddrPrefixInterface { + return dd.nlri +} + +func (dd *DestinationDefault) setNlri(nlri bgp.AddrPrefixInterface) { + dd.nlri = nlri +} + +func (dd *DestinationDefault) getBestPathReason() string { + return dd.bestPathReason +} + +func (dd *DestinationDefault) setBestPathReason(reason string) { + dd.bestPathReason = reason +} + +func (dd *DestinationDefault) getBestPath() Path { + return dd.bestPath +} + +func (dd *DestinationDefault) setBestPath(path Path) { + dd.bestPath = path +} + +func (dd *DestinationDefault) getKnownPathList() []Path { + return dd.knownPathList +} + +func (dd *DestinationDefault) addWithdraw(withdraw Path) { + dd.validatePath(withdraw) + dd.withdrawList = append(dd.withdrawList, withdraw) +} +func (dd *DestinationDefault) addNewPath(newPath Path) { + dd.validatePath(newPath) + dd.newPathList = append(dd.newPathList, newPath) +} +func (dd *DestinationDefault) addSentRoute(sentRoute *SentRoute) { + dd.sentRoutes[sentRoute.peer] = sentRoute +} +func (dd *DestinationDefault) removeSentRoute(peer *Peer) bool { + if dd.wasSentTo(peer) { + delete(dd.sentRoutes, peer) + return true + } + return false +} +func (dd *DestinationDefault) wasSentTo(peer *Peer) bool { + _, ok := dd.sentRoutes[peer] + if ok { + return true + } + return false +} +func (dd *DestinationDefault) removeOldPathsFromSource(source *Peer) []Path { + removePaths := make([]Path, 0) + sourceVerNum := source.VersionNum + tempKnownPathList := make([]Path, 0) + + for _, path := range dd.knownPathList { + if path.getSource() == source && path.getSourceVerNum() < sourceVerNum { + removePaths = append(removePaths, path) + } else { + tempKnownPathList = append(tempKnownPathList, path) + } + } + dd.knownPathList = tempKnownPathList + return removePaths +} + +func (dd *DestinationDefault) validatePath(path Path) { + if path == nil || path.getRouteFamily() != dd.ROUTE_FAMILY { + logger.Error("Invalid path. Expected %s path got %s.", dd.ROUTE_FAMILY, path) + } +} + +// Calculates best-path among known paths for this destination. +// +// Returns: - Best path +// +// Modifies destination's state related to stored paths. Removes withdrawn +// paths from known paths. Also, adds new paths to known paths. +func (dest *DestinationDefault) Calculate(localAsn uint32) (Path, string, error) { + + // First remove the withdrawn paths. + // Note: If we want to support multiple paths per destination we may + // have to maintain sent-routes per path. + dest.removeWithdrawls() + + // Have to select best-path from available paths and new paths. + // If we do not have any paths, then we no longer have best path. + if len(dest.knownPathList) == 0 && len(dest.newPathList) == 1 { + // If we do not have any old but one new path + // it becomes best path. + dest.knownPathList = append(dest.knownPathList, dest.newPathList[0]) + dest.newPathList, _ = deleteAt(dest.newPathList, 0) + logger.Debugf("best path : %s, reason=%s", dest.knownPathList[0], BPR_ONLY_PATH) + + return dest.knownPathList[0], BPR_ONLY_PATH, nil + } + + // If we have a new version of old/known path we use it and delete old + // one. + dest.removeOldPaths() + logger.Debugf("removeOldPaths") + // Collect all new paths into known paths. + dest.knownPathList = append(dest.knownPathList, dest.newPathList...) + + // Clear new paths as we copied them. + dest.newPathList = make([]Path, 0) + + // If we do not have any paths to this destination, then we do not have + // new best path. + if len(dest.knownPathList) == 0 { + return nil, BPR_UNKNOWN, nil + } + + // Compute new best path + currentBestPath, reason, e := dest.computeKnownBestPath(localAsn) + if e != nil { + logger.Error(e) + } + return currentBestPath, reason, e + +} + +//"""Removes withdrawn paths. +// +//Note: +//We may have disproportionate number of withdraws compared to know paths +//since not all paths get installed into the table due to bgp policy and +//we can receive withdraws for such paths and withdrawals may not be +//stopped by the same policies. +//""" +func (dest *DestinationDefault) removeWithdrawls() { + + logger.Debugf("Removing %d withdrawals", len(dest.withdrawList)) + + // If we have no withdrawals, we have nothing to do. + if len(dest.withdrawList) == 0 { + return + } + + // If we have some withdrawals and no know-paths, it means it is safe to + // delete these withdraws. + if len(dest.knownPathList) == 0 { + logger.Debugf("Found %s withdrawals for path(s) that did not get installed", len(dest.withdrawList)) + dest.withdrawList = dest.withdrawList[len(dest.withdrawList):] + } + + // If we have some known paths and some withdrawals, we find matches and + // delete them first. + matches := make(map[string]Path) + wMatches := make(map[string]Path) + // Match all withdrawals from destination paths. + for _, wItem := range dest.withdrawList { + withdraw := wItem.(*PathDefault) + var isFound bool = false + for _, item := range dest.knownPathList { + path := item.(*PathDefault) + // We have a match if the source are same. + // TODO add GetSource to Path interface + if path.source == withdraw.source { + isFound = true + matches[path.String()] = path + wMatches[withdraw.String()] = withdraw + // One withdraw can remove only one path. + break + } + } + + // We do no have any match for this withdraw. + if !isFound { + logger.Debugf("No matching path for withdraw found, may be path was not installed into table: %s", withdraw.String()) + } + } + + // If we have partial match. + if len(matches) != len(dest.withdrawList) { + logger.Debugf( + "Did not find match for some withdrawals. Number of matches(%d), number of withdrawals (%d)", + len(matches), len(dest.withdrawList)) + } + + // Clear matching paths and withdrawals. + for _, path := range matches { + var result bool = false + dest.knownPathList, result = removeWithPath(dest.knownPathList, path) + if !result { + logger.Debugf("could not remove path: %s from knownPathList", path.String()) + } + } + for _, path := range wMatches { + var result bool = false + dest.withdrawList, result = removeWithPath(dest.withdrawList, path) + if !result { + logger.Debugf("could not remove path: %s from withdrawList", path.String()) + } + } +} + +func (dest *DestinationDefault) computeKnownBestPath(localAsn uint32) (Path, string, error) { + + // """Computes the best path among known paths. + // + // Returns current best path among `knownPaths`. + if len(dest.knownPathList) == 0 { + return nil, "", fmt.Errorf("Need at-least one known path to compute best path") + } + + logger.Debugf("computeKnownBestPath known pathlist: %d", len(dest.knownPathList)) + + // We pick the first path as current best path. This helps in breaking + // tie between two new paths learned in one cycle for which best-path + // calculation steps lead to tie. + currentBestPath := dest.knownPathList[0] + bestPathReason := BPR_ONLY_PATH + for _, nextPath := range dest.knownPathList[1:] { + // Compare next path with current best path. + // TODO make interface to get Local AS number + newBestPath, reason := computeBestPath(localAsn, currentBestPath, nextPath) + bestPathReason = reason + if newBestPath != nil { + currentBestPath = newBestPath + } + } + return currentBestPath, bestPathReason, nil +} + +func (dest *DestinationDefault) removeOldPaths() { + // """Identifies which of known paths are old and removes them. + // + // Known paths will no longer have paths whose new version is present in + // new paths. + // """ + + newPaths := dest.newPathList + knownPaths := dest.knownPathList + + for _, newPath := range newPaths { + oldPaths := make([]Path, 0) + for _, path := range knownPaths { + // Here we just check if source is same and not check if path + // version num. as newPaths are implicit withdrawal of old + // paths and when doing RouteRefresh (not EnhancedRouteRefresh) + // we get same paths again. + if newPath.getSource() == path.getSource() { + oldPaths = append(oldPaths, path) + break + } + } + for _, oldPath := range oldPaths { + match := false + knownPaths, match = removeWithPath(knownPaths, oldPath) + if !match { + logger.Debugf("not exist withdrawal of old path in known paths: %s ", oldPath.String()) + + } + logger.Debugf("Implicit withdrawal of old path, "+ + "since we have learned new path from same source: %s", oldPath.String()) + } + } + dest.knownPathList = knownPaths +} + +func deleteAt(list []Path, pos int) ([]Path, bool) { + if list != nil { + list = append(list[:pos], list[pos+1:]...) + return list, true + } + return nil, false +} + +// remove item from slice by object itself +func removeWithPath(list []Path, path Path) ([]Path, bool) { + + for index, p := range list { + if p == path { + pathList := append(list[:index], list[index+1:]...) + return pathList, true + } + } + return list, false +} + +func computeBestPath(localAsn uint32, path1, path2 Path) (Path, string) { + + //Compares given paths and returns best path. + // + //Parameters: + // -`localAsn`: asn of local bgpspeaker + // -`path1`: first path to compare + // -`path2`: second path to compare + // + // Best path processing will involve following steps: + // 1. Select a path with a reachable next hop. + // 2. Select the path with the highest weight. + // 3. If path weights are the same, select the path with the highest + // local preference value. + // 4. Prefer locally originated routes (network routes, redistributed + // routes, or aggregated routes) over received routes. + // 5. Select the route with the shortest AS-path length. + // 6. If all paths have the same AS-path length, select the path based + // on origin: IGP is preferred over EGP; EGP is preferred over + // Incomplete. + // 7. If the origins are the same, select the path with lowest MED + // value. + // 8. If the paths have the same MED values, select the path learned + // via EBGP over one learned via IBGP. + // 9. Select the route with the lowest IGP cost to the next hop. + // 10. Select the route received from the peer with the lowest BGP + // router ID. + // + // Returns None if best-path among given paths cannot be computed else best + // path. + // Assumes paths from NC has source equal to None. + // + + var bestPath Path + bestPathReason := BPR_UNKNOWN + + // Follow best path calculation algorithm steps. + // compare by reachability + if bestPath == nil { + bestPath = compareByReachableNexthop(path1, path2) + bestPathReason = BPR_REACHABLE_NEXT_HOP + } + + if bestPath == nil { + bestPath = compareByHighestWeight(path1, path2) + bestPathReason = BPR_HIGHEST_WEIGHT + } + + if bestPath == nil { + bestPath = compareByLocalPref(path1, path2) + bestPathReason = BPR_LOCAL_PREF + } + if bestPath == nil { + bestPath = compareByLocalOrigin(path1, path2) + bestPathReason = BPR_LOCAL_ORIGIN + } + if bestPath == nil { + bestPath = compareByASPath(path1, path2) + bestPathReason = BPR_ASPATH + } + if bestPath == nil { + bestPath = compareByOrigin(path1, path2) + bestPathReason = BPR_ORIGIN + } + if bestPath == nil { + bestPath = compareByMED(path1, path2) + bestPathReason = BPR_MED + } + if bestPath == nil { + bestPath = compareByASNumber(localAsn, path1, path2) + bestPathReason = BPR_ASN + } + if bestPath == nil { + bestPath = compareByIGPCost(path1, path2) + bestPathReason = BPR_IGP_COST + } + if bestPath == nil { + var e error = nil + bestPath, e = compareByRouterID(localAsn, path1, path2) + if e != nil { + logger.Error(e) + } + bestPathReason = BPR_ROUTER_ID + } + if bestPath == nil { + bestPathReason = BPR_UNKNOWN + } + + return bestPath, bestPathReason +} + +func compareByReachableNexthop(path1, path2 Path) Path { + // Compares given paths and selects best path based on reachable next-hop. + // + // If no path matches this criteria, return None. + // However RouteServer doesn't need to check reachability, so return nil. + logger.Debugf("enter compareByReachableNexthop") + logger.Debugf("path1: %s, path2: %s", path1, path2) + return nil +} + +func compareByHighestWeight(path1, path2 Path) Path { + // Selects a path with highest weight. + // + // Weight is BGPS specific parameter. It is local to the router on which it + // is configured. + // Return: + // nil if best path among given paths cannot be decided, else best path. + logger.Debugf("enter compareByHighestWeight") + logger.Debugf("path1: %s, path2: %s", path1, path2) + return nil +} + +func compareByLocalPref(path1, path2 Path) Path { + // Selects a path with highest local-preference. + // + // Unlike the weight attribute, which is only relevant to the local + // router, local preference is an attribute that routers exchange in the + // same AS. Highest local-pref is preferred. If we cannot decide, + // we return None. + // + // # Default local-pref values is 100 + logger.Debugf("enter compareByLocalPref") + attribute1 := path1.getPathAttribute(bgp.BGP_ATTR_TYPE_LOCAL_PREF) + attribute2 := path2.getPathAttribute(bgp.BGP_ATTR_TYPE_LOCAL_PREF) + + if attribute1 == nil || attribute2 == nil { + return nil + } + + localPref1 := attribute1.(*bgp.PathAttributeLocalPref).Value + localPref2 := attribute2.(*bgp.PathAttributeLocalPref).Value + + // Highest local-preference value is preferred. + if localPref1 > localPref2 { + return path1 + } else if localPref1 < localPref2 { + return path2 + } else { + return nil + } +} + +func compareByLocalOrigin(path1, path2 Path) Path { + + // """Select locally originating path as best path. + // Locally originating routes are network routes, redistributed routes, + // or aggregated routes. + // Returns None if given paths have same source. + // """ + // # If both paths are from same sources we cannot compare them here. + logger.Debugf("enter compareByLocalOrigin") + if path1.getSource() == path2.getSource() { + return nil + } + + // # Here we consider prefix from NC as locally originating static route. + // # Hence it is preferred. + if path1.getSource() == nil { + return path1 + } + + if path2.getSource() == nil { + return path2 + } + return nil +} + +func compareByASPath(path1, path2 Path) Path { + // Calculated the best-paths by comparing as-path lengths. + // + // Shortest as-path length is preferred. If both path have same lengths, + // we return None. + logger.Debugf("enter compareByASPath") + attribute1 := path1.getPathAttribute(bgp.BGP_ATTR_TYPE_AS_PATH) + attribute2 := path2.getPathAttribute(bgp.BGP_ATTR_TYPE_AS_PATH) + + asPath1 := attribute1.(*bgp.PathAttributeAsPath) + asPath2 := attribute2.(*bgp.PathAttributeAsPath) + + if asPath1 == nil || asPath2 == nil { + logger.Error("it is not possible to compare asPath are not present") + } + + var l1, l2 int + for _, pathParam := range asPath1.Value { + l1 += pathParam.ASLen() + } + + for _, pathParam := range asPath2.Value { + l2 += pathParam.ASLen() + } + + logger.Debugf("l1: %d, l2: %d", l1, l2) + logger.Debug(reflect.TypeOf(asPath1.Value)) + logger.Debug(asPath1.Value) + if l1 > l2 { + return path2 + } else if l1 < l2 { + return path1 + } else { + return nil + } +} + +func compareByOrigin(path1, path2 Path) Path { + // Select the best path based on origin attribute. + // + // IGP is preferred over EGP; EGP is preferred over Incomplete. + // If both paths have same origin, we return None. + logger.Debugf("enter compareByOrigin") + attribute1 := path1.getPathAttribute(bgp.BGP_ATTR_TYPE_ORIGIN) + attribute2 := path2.getPathAttribute(bgp.BGP_ATTR_TYPE_ORIGIN) + + if attribute1 == nil || attribute2 == nil { + logger.Error("it is not possible to compare origin are not present") + return nil + } + + origin1, n1 := binary.Uvarint(attribute1.(*bgp.PathAttributeOrigin).Value) + origin2, n2 := binary.Uvarint(attribute2.(*bgp.PathAttributeOrigin).Value) + logger.Debugf("path1 origin value: %d, %d byte read", origin1, n1) + logger.Debugf("path2 origin value: %d, %d byte read", origin2, n2) + + // If both paths have same origins + if origin1 == origin2 { + return nil + } else if origin1 < origin2 { + return path1 + } else { + return path2 + } +} + +func compareByMED(path1, path2 Path) Path { + // Select the path based with lowest MED value. + // + // If both paths have same MED, return None. + // By default, a route that arrives with no MED value is treated as if it + // had a MED of 0, the most preferred value. + // RFC says lower MED is preferred over higher MED value. + // compare MED among not only same AS path but also all path, + // like bgp always-compare-med + logger.Debugf("enter compareByMED") + getMed := func(path Path) uint32 { + attribute := path.getPathAttribute(bgp.BGP_ATTR_TYPE_MULTI_EXIT_DISC) + if attribute == nil { + return 0 + } + med := attribute.(*bgp.PathAttributeMultiExitDisc).Value + return med + } + + med1 := getMed(path1) + med2 := getMed(path2) + + if med1 == med2 { + return nil + } else if med1 < med2 { + return path1 + } + return path2 +} + +func compareByASNumber(localAsn uint32, path1, path2 Path) Path { + + //Select the path based on source (iBGP/eBGP) peer. + // + //eBGP path is preferred over iBGP. If both paths are from same kind of + //peers, return None. + logger.Debugf("enter compareByASNumber") + getPathSourceAsn := func(path Path) uint32 { + var asn uint32 + if path.getSource() == nil { + asn = localAsn + } else { + asn = path.getSource().RemoteAs + } + return asn + } + + p1Asn := getPathSourceAsn(path1) + p2Asn := getPathSourceAsn(path2) + // If path1 is from ibgp peer and path2 is from ebgp peer. + if (p1Asn == localAsn) && (p2Asn != localAsn) { + return path2 + } + + // If path2 is from ibgp peer and path1 is from ebgp peer, + if (p2Asn == localAsn) && (p1Asn != localAsn) { + return path1 + } + + // If both paths are from ebgp or ibpg peers, we cannot decide. + return nil +} + +func compareByIGPCost(path1, path2 Path) Path { + // Select the route with the lowest IGP cost to the next hop. + // + // Return None if igp cost is same. + // Currently BGPS has no concept of IGP and IGP cost. + logger.Debugf("enter compareByIGPCost") + logger.Debugf("path1: %s, path2: %s", path1, path2) + return nil +} + +func compareByRouterID(localAsn uint32, path1, path2 Path) (Path, error) { + // Select the route received from the peer with the lowest BGP router ID. + // + // If both paths are eBGP paths, then we do not do any tie breaking, i.e we do + // not pick best-path based on this criteria. + // RFC: http://tools.ietf.org/html/rfc5004 + // We pick best path between two iBGP paths as usual. + logger.Debugf("enter compareByRouterID") + getAsn := func(pathSource *Peer) uint32 { + if pathSource == nil { + return localAsn + } else { + return pathSource.RemoteAs + } + } + + getRouterId := func(pathSource *Peer, localBgpId uint32) uint32 { + if pathSource == nil { + return localBgpId + } else { + routerId := pathSource.protocol.recvOpenMsg.ID + routerId_u32 := binary.BigEndian.Uint32(routerId) + return routerId_u32 + } + } + + pathSource1 := path1.getSource() + pathSource2 := path2.getSource() + + // If both paths are from NC we have same router Id, hence cannot compare. + if pathSource1 == nil && pathSource2 == nil { + return nil, nil + } + + asn1 := getAsn(pathSource1) + asn2 := getAsn(pathSource2) + + isEbgp1 := asn1 != localAsn + isEbgp2 := asn2 != localAsn + // If both paths are from eBGP peers, then according to RFC we need + // not tie break using router id. + if isEbgp1 && isEbgp2 { + return nil, nil + } + + if (isEbgp1 == true && isEbgp2 == false) || + (isEbgp1 == false && isEbgp2 == true) { + return nil, fmt.Errorf("This method does not support comparing ebgp with ibgp path") + } + + // At least one path is not coming from NC, so we get local bgp id. + var localBgpId_u32 uint32 + if pathSource1 != nil { + localBgpId := pathSource1.protocol.sentOpenMsg.ID + localBgpId_u32 = binary.BigEndian.Uint32(localBgpId) + } else { + localBgpId := pathSource2.protocol.sentOpenMsg.ID + localBgpId_u32 = binary.BigEndian.Uint32(localBgpId) + } + + // Get router ids. + routerId1_u32 := getRouterId(pathSource1, localBgpId_u32) + routerId2_u32 := getRouterId(pathSource2, localBgpId_u32) + + // If both router ids are same/equal we cannot decide. + // This case is possible since router ids are arbitrary. + if routerId1_u32 == routerId2_u32 { + return nil, nil + } + + if routerId1_u32 < routerId2_u32 { + return path1, nil + } else { + return path2, nil + } +} + +// return Destination's string representation +func (dest *DestinationDefault) String() string { + str := fmt.Sprintf("Destination NLRI: %s", dest.getPrefix().String()) + return str +} + +func (dest *DestinationDefault) constructWithdrawPath() Path { + path := &IPv4Path{} + return path +} + +func (dest *DestinationDefault) getPrefix() net.IP { + var ip net.IP + switch p := dest.nlri.(type) { + case *bgp.NLRInfo: + ip = p.IPAddrPrefix.IPAddrPrefixDefault.Prefix + case *bgp.WithdrawnRoute: + ip = p.IPAddrPrefix.IPAddrPrefixDefault.Prefix + } + return ip +} + +/* +* Definition of inherited Destination interface + */ + +type IPv4Destination struct { + *DestinationDefault + //need structure +} + +func NewIPv4Destination(nlri bgp.AddrPrefixInterface) *IPv4Destination { + ipv4Destination := &IPv4Destination{} + ipv4Destination.DestinationDefault = NewDestinationDefault(nlri) + ipv4Destination.DestinationDefault.ROUTE_FAMILY = RF_IPv4_UC + //need Processing + return ipv4Destination +} + +func (ipv4d *IPv4Destination) String() string { + str := fmt.Sprintf("Destination NLRI: %s", ipv4d.getPrefix().String()) + return str +} + +type IPv6Destination struct { + *DestinationDefault + //need structure +} + +func NewIPv6Destination(nlri bgp.AddrPrefixInterface) *IPv6Destination { + ipv6Destination := &IPv6Destination{} + ipv6Destination.DestinationDefault = NewDestinationDefault(nlri) + ipv6Destination.DestinationDefault.ROUTE_FAMILY = RF_IPv6_UC + //need Processing + return ipv6Destination +} + +func (ipv6d *IPv6Destination) String() string { + + str := fmt.Sprintf("Destination NLRI: %s", ipv6d.getPrefix().String()) + return str +} + +func (ipv6d *IPv6Destination) getPrefix() net.IP { + var ip net.IP + logger.Debugf("type %s", reflect.TypeOf(ipv6d.nlri)) + switch p := ipv6d.nlri.(type) { + case *bgp.IPv6AddrPrefix: + ip = p.IPAddrPrefix.IPAddrPrefixDefault.Prefix + case *bgp.WithdrawnRoute: + ip = p.IPAddrPrefix.IPAddrPrefixDefault.Prefix + } + return ip +} diff --git a/table/destination_test.go b/table/destination_test.go new file mode 100644 index 00000000..03b839a8 --- /dev/null +++ b/table/destination_test.go @@ -0,0 +1,207 @@ +// 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 table + +import ( + //"fmt" + "github.com/osrg/gobgp/packet" + "github.com/stretchr/testify/assert" + //"net" + "testing" +) + +func updateMsgD1() *bgp.BGPMessage { + + origin := bgp.NewPathAttributeOrigin(0) + aspathParam := []bgp.AsPathParamInterface{bgp.NewAsPathParam(2, []uint16{65000})} + aspath := bgp.NewPathAttributeAsPath(aspathParam) + nexthop := bgp.NewPathAttributeNextHop("192.168.50.1") + med := bgp.NewPathAttributeMultiExitDisc(0) + + pathAttributes := []bgp.PathAttributeInterface{ + origin, + aspath, + nexthop, + med, + } + + nlri := []bgp.NLRInfo{*bgp.NewNLRInfo(24, "10.10.10.0")} + withdrawnRoutes := []bgp.WithdrawnRoute{} + return bgp.NewBGPUpdateMessage(withdrawnRoutes, pathAttributes, nlri) +} + +func updateMsgD2() *bgp.BGPMessage { + + origin := bgp.NewPathAttributeOrigin(0) + aspathParam := []bgp.AsPathParamInterface{bgp.NewAsPathParam(2, []uint16{65100})} + aspath := bgp.NewPathAttributeAsPath(aspathParam) + nexthop := bgp.NewPathAttributeNextHop("192.168.100.1") + med := bgp.NewPathAttributeMultiExitDisc(100) + + pathAttributes := []bgp.PathAttributeInterface{ + origin, + aspath, + nexthop, + med, + } + + nlri := []bgp.NLRInfo{*bgp.NewNLRInfo(24, "20.20.20.0")} + withdrawnRoutes := []bgp.WithdrawnRoute{} + return bgp.NewBGPUpdateMessage(withdrawnRoutes, pathAttributes, nlri) +} +func updateMsgD3() *bgp.BGPMessage { + origin := bgp.NewPathAttributeOrigin(0) + aspathParam := []bgp.AsPathParamInterface{bgp.NewAsPathParam(2, []uint16{65100})} + aspath := bgp.NewPathAttributeAsPath(aspathParam) + nexthop := bgp.NewPathAttributeNextHop("192.168.150.1") + med := bgp.NewPathAttributeMultiExitDisc(100) + + pathAttributes := []bgp.PathAttributeInterface{ + origin, + aspath, + nexthop, + med, + } + + nlri := []bgp.NLRInfo{*bgp.NewNLRInfo(24, "30.30.30.0")} + w1 := bgp.WithdrawnRoute{*bgp.NewIPAddrPrefix(23, "40.40.40.0")} + withdrawnRoutes := []bgp.WithdrawnRoute{w1} + return bgp.NewBGPUpdateMessage(withdrawnRoutes, pathAttributes, nlri) +} +func createDestinationCheck(t *testing.T, path Path) (Destination, string) { + dest := NewIPv4Destination(path.getNlri()) + ar := assert.NotNil(t, dest) + if !ar { + return nil, "NG" + } + return dest, "OK" +} + +func routeCheck(t *testing.T, dest Destination) string { + bgpMsg1 := updateMsgD1() + bgpMsg2 := updateMsgD2() + bgpMsg3 := updateMsgD3() + pr1 := &Peer{VersionNum: 2, RemoteAs: 65000} + pr2 := &Peer{VersionNum: 4, RemoteAs: 65001} + pr3 := &Peer{VersionNum: 4, RemoteAs: 65002} + msg1 := &ProcessMessage{innerMessage: bgpMsg1, fromPeer: pr1} + msg2 := &ProcessMessage{innerMessage: bgpMsg2, fromPeer: pr1} + msg3 := &ProcessMessage{innerMessage: bgpMsg3, fromPeer: pr2} + path1, _ := createPathCheck(t, msg1) + path2, _ := createPathCheck(t, msg2) + path3, _ := createPathCheck(t, msg3) + + //best path selection + dest.addNewPath(path1) + dest.addNewPath(path2) + dest.addNewPath(path3) + dest.addWithdraw(path3) + + _, _, e := dest.Calculate(uint32(100)) + //bpath, str, e := dest.Calculate() + //t.Log(bpath) + //t.Log(str) + ar := assert.Nil(t, e) + if !ar { + return "NG" + } + + //sent route and remove sent route + sroute1 := &SentRoute{path: path1, peer: pr1} + sroute2 := &SentRoute{path: path2, peer: pr1} + sroute3 := &SentRoute{path: path3, peer: pr2} + dest.addSentRoute(sroute1) + dest.addSentRoute(sroute2) + dest.addSentRoute(sroute3) + result := dest.removeSentRoute(pr3) + ar = assert.Equal(t, result, false) + if !ar { + return "NG" + } + result = dest.removeSentRoute(pr2) + ar = assert.Equal(t, result, true) + if !ar { + return "NG" + } + + //remote old path + rpath := dest.removeOldPathsFromSource(pr1) + t.Log(rpath) + //t.Log(dest.getKnownPathList()) + return "OK" +} + +/* +func getKnownPathListCheck(t *testing.T, dest Destination) string { + paths := dest.getKnownPathList() + t.Log(paths) + return "OK" +} +*/ + +//getter&setter test +func dgsTerCheck(t *testing.T, path Path) string { + dd := &DestinationDefault{} + //check Route Family + dd.setRouteFamily(RF_IPv4_UC) + rf := dd.getRouteFamily() + ar := assert.Equal(t, rf, RF_IPv4_UC) + if !ar { + return "NG" + } + //check nlri + nlri := bgp.NewNLRInfo(24, "13.2.3.1") + dd.setNlri(nlri) + r_nlri := dd.getNlri() + ar = assert.Equal(t, r_nlri, nlri) + if !ar { + return "NG" + } + // check best path reason + reason := "reason" + dd.setBestPathReason(reason) + r_reason := dd.getBestPathReason() + ar = assert.Equal(t, r_reason, reason) + if !ar { + return "NG" + } + //check best path + dd.setBestPath(path) + r_path := dd.getBestPath() + ar = assert.Equal(t, r_path, path) + if !ar { + return "NG" + } + return "OK" +} +func TestDestination(t *testing.T) { + bgpMsg1 := updateMsgD1() + pr1 := &Peer{VersionNum: 2, RemoteAs: 65000} + msg := &ProcessMessage{innerMessage: bgpMsg1, fromPeer: pr1} + path, _ := createPathCheck(t, msg) + t.Log("# CREATE PATH CHECK") + dest, result := createDestinationCheck(t, path) + t.Log("# CHECK END -> [ ", result, " ]") + t.Log("") + t.Log("# ROUTE CHECK") + result = routeCheck(t, dest) + t.Log("# CHECK END -> [ ", result, " ]") + t.Log("") + t.Log("# GETTER SETTER CHECK") + result = dgsTerCheck(t, path) + t.Log("# CHECK END -> [ ", result, " ]") + t.Log("") +} diff --git a/table/path.go b/table/path.go new file mode 100644 index 00000000..93d09d8f --- /dev/null +++ b/table/path.go @@ -0,0 +1,339 @@ +// 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 table + +import ( + "fmt" + "github.com/osrg/gobgp/packet" + "github.com/osrg/gobgp/utils" + "net" +) + +type Path interface { + String() string + getPathAttributeMap() *utils.OrderedMap + getPathAttribute(int) bgp.PathAttributeInterface + clone(forWithdrawal bool) Path + setRouteFamily(ROUTE_FAMILY RouteFamily) + getRouteFamily() RouteFamily + setSource(source *Peer) + getSource() *Peer + setNexthop(nexthop net.IP) + getNexthop() net.IP + setSourceVerNum(sourceVerNum int) + getSourceVerNum() int + setWithdraw(withdraw bool) + isWithdraw() bool + setNlri(nlri bgp.AddrPrefixInterface) + getNlri() bgp.AddrPrefixInterface + getPrefix() net.IP + setMedSetByTargetNeighbor(medSetByTargetNeighbor bool) + getMedSetByTargetNeighbor() bool +} + +type PathDefault struct { + ROUTE_FAMILY RouteFamily + source *Peer + nexthop net.IP + sourceVerNum int + withdraw bool + nlri bgp.AddrPrefixInterface + pattrMap *utils.OrderedMap + medSetByTargetNeighbor bool +} + +func NewPathDefault(source *Peer, nlri bgp.AddrPrefixInterface, sourceVerNum int, nexthop net.IP, + isWithdraw bool, pattr *utils.OrderedMap, medSetByTargetNeighbor bool) *PathDefault { + if source == nil { + logger.Error("Need to provide source") + return nil + } + + if !isWithdraw && (pattr == nil || nexthop == nil) { + logger.Error("Need to provide nexthop and patattrs for path that is not a withdraw.") + return nil + } + + path := &PathDefault{} + path.ROUTE_FAMILY = RF_IPv4_UC + path.pattrMap = utils.NewOrderedMap() + if pattr != nil { + keyList := pattr.KeyLists() + for key := keyList.Front(); key != nil; key = key.Next() { + key := key.Value + val := pattr.Get(key) + e := path.pattrMap.Append(key, val) + if e != nil { + logger.Error(e) + } + } + } + path.nlri = nlri + path.source = source + path.nexthop = nexthop + path.sourceVerNum = sourceVerNum + path.withdraw = isWithdraw + path.medSetByTargetNeighbor = medSetByTargetNeighbor + + return path +} + +func (pd *PathDefault) setRouteFamily(ROUTE_FAMILY RouteFamily) { + pd.ROUTE_FAMILY = ROUTE_FAMILY +} +func (pd *PathDefault) getRouteFamily() RouteFamily { + return pd.ROUTE_FAMILY +} + +func (pd *PathDefault) setSource(source *Peer) { + pd.source = source +} +func (pd *PathDefault) getSource() *Peer { + return pd.source +} + +func (pd *PathDefault) setNexthop(nexthop net.IP) { + pd.nexthop = nexthop +} + +func (pd *PathDefault) getNexthop() net.IP { + return pd.nexthop +} + +func (pd *PathDefault) setSourceVerNum(sourceVerNum int) { + pd.sourceVerNum = sourceVerNum +} + +func (pd *PathDefault) getSourceVerNum() int { + return pd.sourceVerNum +} + +func (pd *PathDefault) setWithdraw(withdraw bool) { + pd.withdraw = withdraw +} + +func (pd *PathDefault) isWithdraw() bool { + return pd.withdraw +} + +func (pd *PathDefault) setNlri(nlri bgp.AddrPrefixInterface) { + pd.nlri = nlri +} + +func (pd *PathDefault) getNlri() bgp.AddrPrefixInterface { + return pd.nlri +} + +func (pd *PathDefault) setMedSetByTargetNeighbor(medSetByTargetNeighbor bool) { + pd.medSetByTargetNeighbor = medSetByTargetNeighbor +} + +func (pd *PathDefault) getMedSetByTargetNeighbor() bool { + return pd.medSetByTargetNeighbor +} + +//Copy the entity +func (pd *PathDefault) getPathAttributeMap() *utils.OrderedMap { + cpPattr := utils.NewOrderedMap() + keyList := pd.pattrMap.KeyLists() + for key := keyList.Front(); key != nil; key = key.Next() { + key := key.Value + val := pd.pattrMap.Get(key) + e := cpPattr.Append(key, val) + if e != nil { + logger.Error(e) + } + } + return cpPattr +} + +func (pd *PathDefault) getPathAttribute(pattrType int) bgp.PathAttributeInterface { + attr := pd.pattrMap.Get(pattrType) + if attr == nil { + logger.Debugf("Attribute Type %s is not found", AttributeType(pattrType)) + return nil + } + return attr.(bgp.PathAttributeInterface) +} + +func (pi *PathDefault) clone(forWithdrawal bool) Path { + pathAttrs := utils.NewOrderedMap() + if !forWithdrawal { + pathAttrs = pi.getPathAttributeMap() + } + clone := NewPathDefault(pi.getSource(), pi.getNlri(), pi.getSourceVerNum(), + pi.getNexthop(), forWithdrawal, pathAttrs, pi.getMedSetByTargetNeighbor()) + return clone +} + +// return Path's string representation +func (pi *PathDefault) String() string { + str := fmt.Sprintf("IPv4Path Source: %d, ", pi.getSourceVerNum()) + str = str + fmt.Sprintf(" NLRI: %s, ", pi.getPrefix().String()) + str = str + fmt.Sprintf(" nexthop: %s, ", pi.getNexthop().String()) + str = str + fmt.Sprintf(" withdraw: %s, ", pi.isWithdraw()) + str = str + fmt.Sprintf(" path attributes: %s, ", pi.getPathAttributeMap()) + return str +} + +func (pi *PathDefault) getPrefix() net.IP { + addrPrefix := pi.nlri.(*bgp.NLRInfo) + return addrPrefix.Prefix +} + +func getNextHop(pathAttributes []bgp.PathAttributeInterface) net.IP { + + for _, pathAttr := range pathAttributes { + switch p := pathAttr.(type) { + case *bgp.PathAttributeNextHop: + return p.Value + } + } + return nil + +} + +func createPathAttributeMap(pathAttributes []bgp.PathAttributeInterface) *utils.OrderedMap { + + pathAttrMap := utils.NewOrderedMap() + for _, attr := range pathAttributes { + var err error + switch a := attr.(type) { + case *bgp.PathAttributeOrigin: + err = pathAttrMap.Append(bgp.BGP_ATTR_TYPE_ORIGIN, a) + case *bgp.PathAttributeAsPath: + err = pathAttrMap.Append(bgp.BGP_ATTR_TYPE_AS_PATH, a) + case *bgp.PathAttributeNextHop: + err = pathAttrMap.Append(bgp.BGP_ATTR_TYPE_NEXT_HOP, a) + case *bgp.PathAttributeMultiExitDisc: + err = pathAttrMap.Append(bgp.BGP_ATTR_TYPE_MULTI_EXIT_DISC, a) + case *bgp.PathAttributeLocalPref: + err = pathAttrMap.Append(bgp.BGP_ATTR_TYPE_LOCAL_PREF, a) + case *bgp.PathAttributeAtomicAggregate: + err = pathAttrMap.Append(bgp.BGP_ATTR_TYPE_ATOMIC_AGGREGATE, a) + case *bgp.PathAttributeAggregator: + err = pathAttrMap.Append(bgp.BGP_ATTR_TYPE_AGGREGATOR, a) + case *bgp.PathAttributeCommunities: + err = pathAttrMap.Append(bgp.BGP_ATTR_TYPE_COMMUNITIES, a) + case *bgp.PathAttributeOriginatorId: + err = pathAttrMap.Append(bgp.BGP_ATTR_TYPE_ORIGINATOR_ID, a) + case *bgp.PathAttributeClusterList: + err = pathAttrMap.Append(bgp.BGP_ATTR_TYPE_CLUSTER_LIST, a) + case *bgp.PathAttributeMpReachNLRI: + err = pathAttrMap.Append(bgp.BGP_ATTR_TYPE_MP_REACH_NLRI, a) + case *bgp.PathAttributeMpUnreachNLRI: + err = pathAttrMap.Append(bgp.BGP_ATTR_TYPE_MP_UNREACH_NLRI, a) + case *bgp.PathAttributeExtendedCommunities: + err = pathAttrMap.Append(bgp.BGP_ATTR_TYPE_EXTENDED_COMMUNITIES, a) + case *bgp.PathAttributeAs4Path: + err = pathAttrMap.Append(bgp.BGP_ATTR_TYPE_AS4_PATH, a) + case *bgp.PathAttributeAs4Aggregator: + err = pathAttrMap.Append(bgp.BGP_ATTR_TYPE_AS4_AGGREGATOR, a) + } + if err != nil { + return nil + } + } + return pathAttrMap +} + +// create Path object based on route family +func CreatePath(source *Peer, nlri bgp.AddrPrefixInterface, + pathAttributes []bgp.PathAttributeInterface, isWithdraw bool) Path { + + rf := RouteFamily(int(nlri.AFI())<<16 | int(nlri.SAFI())) + logger.Debugf("afi: %d, safi: %d ", int(nlri.AFI()), nlri.SAFI()) + pathAttrMap := createPathAttributeMap(pathAttributes) + var path Path + + switch rf { + case RF_IPv4_UC: + logger.Debugf("RouteFamily : %s", RF_IPv4_UC.String()) + nexthop := getNextHop(pathAttributes) + path = NewIPv4Path(source, nlri, source.VersionNum, nexthop, isWithdraw, pathAttrMap, false) + case RF_IPv6_UC: + logger.Debugf("RouteFamily : %s", RF_IPv6_UC.String()) + var mpattr *bgp.PathAttributeMpReachNLRI + var nexthop net.IP + + if !isWithdraw { + mpattr = pathAttrMap.Get(bgp.BGP_ATTR_TYPE_MP_REACH_NLRI).(*bgp.PathAttributeMpReachNLRI) + nexthop = mpattr.Nexthop + } else { + nexthop = nil + } + + path = NewIPv6Path(source, nlri, source.VersionNum, nexthop, isWithdraw, pathAttrMap, false) + } + return path +} + +/* +* Definition of inherited Path interface + */ +type IPv4Path struct { + *PathDefault +} + +func NewIPv4Path(source *Peer, nlri bgp.AddrPrefixInterface, sourceVerNum int, nexthop net.IP, + isWithdraw bool, pattr *utils.OrderedMap, medSetByTargetNeighbor bool) *IPv4Path { + ipv4Path := &IPv4Path{} + ipv4Path.PathDefault = NewPathDefault(source, nlri, sourceVerNum, nexthop, isWithdraw, pattr, medSetByTargetNeighbor) + ipv4Path.PathDefault.ROUTE_FAMILY = RF_IPv4_UC + return ipv4Path +} + +func (ipv4p *IPv4Path) setPathDefault(pd *PathDefault) { + ipv4p.PathDefault = pd +} +func (ipv4p *IPv4Path) getPathDefault() *PathDefault { + return ipv4p.PathDefault +} + +type IPv6Path struct { + *PathDefault +} + +func NewIPv6Path(source *Peer, nlri bgp.AddrPrefixInterface, sourceVerNum int, nexthop net.IP, + isWithdraw bool, pattr *utils.OrderedMap, medSetByTargetNeighbor bool) *IPv6Path { + ipv6Path := &IPv6Path{} + ipv6Path.PathDefault = NewPathDefault(source, nlri, sourceVerNum, nexthop, isWithdraw, pattr, medSetByTargetNeighbor) + ipv6Path.PathDefault.ROUTE_FAMILY = RF_IPv6_UC + return ipv6Path +} + +func (ipv6p *IPv6Path) setPathDefault(pd *PathDefault) { + ipv6p.PathDefault = pd +} + +func (ipv6p *IPv6Path) getPathDefault() *PathDefault { + return ipv6p.PathDefault +} + +func (ipv6p *IPv6Path) getPrefix() net.IP { + addrPrefix := ipv6p.nlri.(*bgp.IPv6AddrPrefix) + return addrPrefix.Prefix +} + +// return IPv6Path's string representation +func (ipv6p *IPv6Path) String() string { + str := fmt.Sprintf("IPv6Path Source: %d, ", ipv6p.getSourceVerNum()) + str = str + fmt.Sprintf(" NLRI: %s, ", ipv6p.getPrefix().String()) + str = str + fmt.Sprintf(" nexthop: %s, ", ipv6p.getNexthop().String()) + str = str + fmt.Sprintf(" withdraw: %s, ", ipv6p.isWithdraw()) + str = str + fmt.Sprintf(" path attributes: %s, ", ipv6p.getPathAttributeMap()) + return str +} diff --git a/table/path_test.go b/table/path_test.go new file mode 100644 index 00000000..d15a20f2 --- /dev/null +++ b/table/path_test.go @@ -0,0 +1,270 @@ +// path_test.go +package table + +import ( + //"fmt" + "github.com/osrg/gobgp/packet" + "github.com/stretchr/testify/assert" + "net" + "testing" +) + +func updateMsg() *bgp.BGPMessage { + w1 := bgp.WithdrawnRoute{*bgp.NewIPAddrPrefix(23, "121.1.3.2")} + w2 := bgp.WithdrawnRoute{*bgp.NewIPAddrPrefix(17, "100.33.3.0")} + w := []bgp.WithdrawnRoute{w1, w2} + //w := []WithdrawnRoute{} + + aspath1 := []bgp.AsPathParamInterface{ + bgp.NewAsPathParam(2, []uint16{1000}), + bgp.NewAsPathParam(1, []uint16{1001, 1002}), + bgp.NewAsPathParam(2, []uint16{1003, 1004}), + } + + aspath2 := []bgp.AsPathParamInterface{ + bgp.NewAs4PathParam(2, []uint32{1000000}), + bgp.NewAs4PathParam(1, []uint32{1000001, 1002}), + bgp.NewAs4PathParam(2, []uint32{1003, 100004}), + } + + aspath3 := []*bgp.As4PathParam{ + bgp.NewAs4PathParam(2, []uint32{1000000}), + bgp.NewAs4PathParam(1, []uint32{1000001, 1002}), + bgp.NewAs4PathParam(2, []uint32{1003, 100004}), + } + + ecommunities := []bgp.ExtendedCommunityInterface{ + &bgp.TwoOctetAsSpecificExtended{SubType: 1, AS: 10003, LocalAdmin: 3 << 20}, + &bgp.FourOctetAsSpecificExtended{SubType: 2, AS: 1 << 20, LocalAdmin: 300}, + &bgp.IPv4AddressSpecificExtended{SubType: 3, IPv4: net.ParseIP("192.2.1.2").To4(), LocalAdmin: 3000}, + &bgp.OpaqueExtended{Value: []byte{0, 1, 2, 3, 4, 5, 6, 7}}, + &bgp.UnknownExtended{Type: 99, Value: []byte{0, 1, 2, 3, 4, 5, 6, 7}}, + } + + mp_nlri := []bgp.AddrPrefixInterface{ + bgp.NewLabelledVPNIPAddrPrefix(20, "192.0.9.0", *bgp.NewLabel(1, 2, 3), + bgp.NewRouteDistinguisherTwoOctetAS(256, 10000)), + bgp.NewLabelledVPNIPAddrPrefix(26, "192.10.8.192", *bgp.NewLabel(5, 6, 7, 8), + bgp.NewRouteDistinguisherIPAddressAS("10.0.1.1", 10001)), + } + + mp_nlri2 := []bgp.AddrPrefixInterface{bgp.NewIPv6AddrPrefix(100, + "fe80:1234:1234:5667:8967:af12:8912:1023")} + + mp_nlri3 := []bgp.AddrPrefixInterface{bgp.NewLabelledVPNIPv6AddrPrefix(100, + "fe80:1234:1234:5667:8967:af12:1203:33a1", *bgp.NewLabel(5, 6), + bgp.NewRouteDistinguisherFourOctetAS(5, 6))} + + mp_nlri4 := []bgp.AddrPrefixInterface{bgp.NewLabelledIPAddrPrefix(25, "192.168.0.0", + *bgp.NewLabel(5, 6, 7))} + + p := []bgp.PathAttributeInterface{ + bgp.NewPathAttributeOrigin(3), + bgp.NewPathAttributeAsPath(aspath1), + bgp.NewPathAttributeAsPath(aspath2), + bgp.NewPathAttributeNextHop("129.1.1.2"), + bgp.NewPathAttributeMultiExitDisc(1 << 20), + bgp.NewPathAttributeLocalPref(1 << 22), + bgp.NewPathAttributeAtomicAggregate(), + bgp.NewPathAttributeAggregator(uint16(30002), "129.0.2.99"), + bgp.NewPathAttributeAggregator(uint32(30002), "129.0.2.99"), + bgp.NewPathAttributeAggregator(uint32(300020), "129.0.2.99"), + bgp.NewPathAttributeCommunities([]uint32{1, 3}), + bgp.NewPathAttributeOriginatorId("10.10.0.1"), + bgp.NewPathAttributeClusterList([]string{"10.10.0.2", "10.10.0.3"}), + bgp.NewPathAttributeExtendedCommunities(ecommunities), + bgp.NewPathAttributeAs4Path(aspath3), + bgp.NewPathAttributeAs4Aggregator(10000, "112.22.2.1"), + bgp.NewPathAttributeMpReachNLRI("112.22.2.0", mp_nlri), + bgp.NewPathAttributeMpReachNLRI("1023::", mp_nlri2), + bgp.NewPathAttributeMpReachNLRI("fe80::", mp_nlri3), + bgp.NewPathAttributeMpReachNLRI("129.1.1.1", mp_nlri4), + bgp.NewPathAttributeMpUnreachNLRI(mp_nlri), + &bgp.PathAttributeUnknown{ + PathAttribute: bgp.PathAttribute{ + Flags: 1, + Type: 100, + Value: []byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}, + }, + }, + } + n := []bgp.NLRInfo{*bgp.NewNLRInfo(24, "13.2.3.1"), *bgp.NewNLRInfo(24, "13.2.3.2")} + //n := []bgp.NLRInfo{*bgp.NewNLRInfo(100, "fe80:1234:1234:5667:8967:af12:1203:33a1")} + return bgp.NewBGPUpdateMessage(w, p, n) +} + +func initMsg() *ProcessMessage { + bgpMessage := updateMsg() + peer := &Peer{} + peer.VersionNum = 4 + peer.RemoteAs = 65000 + + msg := &ProcessMessage{ + innerMessage: bgpMessage, + fromPeer: peer, + } + return msg +} +func createPathCheck(t *testing.T, msg *ProcessMessage) (Path, string) { + updateMsg := msg.innerMessage.Body.(*bgp.BGPUpdate) + nlriList := updateMsg.NLRI + pathAttributes := updateMsg.PathAttributes + nlri_info := nlriList[0] + path := CreatePath(msg.fromPeer, &nlri_info, pathAttributes, false) + ar := assert.NotNil(t, path, "Path is Nil") + if !ar { + return nil, "NG" + } + return path, "OK" +} +func getNextHopCheck(t *testing.T, msg *ProcessMessage) string { + nexthop := "129.1.1.2" + pAttr := msg.innerMessage.Body.(*bgp.BGPUpdate).PathAttributes + r_nexthop := getNextHop(pAttr) + ar := assert.Equal(t, r_nexthop.String(), nexthop, "unmatch nexthop") + if !ar { + return "NG" + } + return "OK" +} +func getPrefixCheck(t *testing.T, path Path) string { + prefix := "13.2.3.1" + //prefix := "fe80:1234:1234:5667:8967:af12:1203:33a1" + r_prefix := path.getPrefix() + ar := assert.Equal(t, r_prefix.String(), prefix, "unmatch prefix") + if !ar { + return "NG" + } + return "OK" +} + +func getPathAttributeCheck(t *testing.T, path Path) string { + nh := "129.1.1.2" + pa := path.getPathAttribute(bgp.BGP_ATTR_TYPE_NEXT_HOP) + r_nh := pa.(*bgp.PathAttributeNextHop).Value.String() + ar := assert.Equal(t, r_nh, nh, "unmatch nexthop") + if !ar { + return "NG" + } + return "OK" +} + +func cloneCheck(t *testing.T, path Path) string { + + prefix := path.getPrefix() + cl := path.clone(false) + r_prefix := cl.getPrefix() + ar := assert.Equal(t, r_prefix, prefix, "unmatch prefix in clone element") + if !ar { + return "NG" + } + return "OK" +} + +//getter&setter test +func pgsTerCheck(t *testing.T) string { + pd := &PathDefault{} + //check Route Family + pd.setRouteFamily(RF_IPv4_UC) + rf := pd.getRouteFamily() + ar := assert.Equal(t, rf, RF_IPv4_UC, "unmatch route family") + if !ar { + return "NG" + } + + //check source + pr := &Peer{ + RemoteAs: 65000, + VersionNum: 4, + } + pd.setSource(pr) + r_pr := pd.getSource() + ar = assert.Equal(t, r_pr, pr, "unmatch source") + if !ar { + return "NG" + } + //check nexthop + ip := net.ParseIP("192.168.0.1") + pd.setNexthop(ip) + nh := pd.getNexthop() + ar = assert.Equal(t, nh, ip, "unmatch nexthop") + if !ar { + return "NG" + } + //check source version num + svn := 4 + pd.setSourceVerNum(svn) + r_svn := pd.getSourceVerNum() + ar = assert.Equal(t, r_svn, svn, "unmatch source ver num") + if !ar { + return "NG" + } + //check wighdrow + wd := true + pd.setWithdraw(wd) + r_wd := pd.isWithdraw() + ar = assert.Equal(t, r_wd, wd, "unmatch withdrow flg") + if !ar { + return "NG" + } + //check nlri + nlri := bgp.NewNLRInfo(24, "13.2.3.1") + pd.setNlri(nlri) + r_nlri := pd.getNlri() + ar = assert.Equal(t, r_nlri, nlri, "unmatch nlri") + if !ar { + return "NG" + } + //check med set by targetNeighbor + msbt := true + pd.setMedSetByTargetNeighbor(msbt) + r_msbt := pd.getMedSetByTargetNeighbor() + ar = assert.Equal(t, r_msbt, msbt, "unmatch med flg") + if !ar { + return "NG" + } + //ipv4 pathDefault + ipv4 := &IPv4Path{} + ipv4.setPathDefault(pd) + r_pd4 := ipv4.getPathDefault() + ar = assert.Equal(t, r_pd4, pd, "unmatch path default") + if !ar { + return "NG" + } + //ipv6 pathDefault + ipv6 := &IPv6Path{} + ipv6.setPathDefault(pd) + r_pd6 := ipv6.getPathDefault() + ar = assert.Equal(t, r_pd6, pd, "unmatch path default") + if !ar { + return "NG" + } + return "OK" +} +func TestPath(t *testing.T) { + msg := initMsg() + t.Log("# CREATE PATH CHECK") + path, result := createPathCheck(t, msg) + t.Log("# CHECK END -> [ ", result, " ]") + t.Log("") + t.Log("# GET NEXTHOP CHECK") + result = getNextHopCheck(t, msg) + t.Log("# CHECK END -> [ ", result, " ]") + t.Log("") + t.Log("# GET PREFIX CHECK") + result = getPrefixCheck(t, path) + t.Log("# CHECK END -> [ ", result, " ]") + t.Log("") + t.Log("# GET PATH ATTRIBUTE CHECK") + result = getPathAttributeCheck(t, path) + t.Log("# CHECK END -> [ ", result, " ]") + t.Log("") + t.Log("# CLONE CHECK") + result = cloneCheck(t, path) + t.Log("# CHECK END -> [ ", result, " ]") + t.Log("") + t.Log("# GETTER SETTER CHECK") + result = pgsTerCheck(t) + t.Log("# CHECK END -> [ ", result, " ]") + t.Log("") +} diff --git a/table/table.go b/table/table.go new file mode 100644 index 00000000..22554eba --- /dev/null +++ b/table/table.go @@ -0,0 +1,252 @@ +// 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 table + +import ( + "github.com/osrg/gobgp/packet" + "net" + "reflect" +) + +type Table interface { + createDest(nlri bgp.AddrPrefixInterface) Destination + getDestinations() map[string]Destination + setDestinations(destinations map[string]Destination) + getDestination(key string) Destination + setDestination(key string, dest Destination) + tableKey(nlri bgp.AddrPrefixInterface) net.IP + validatePath(path Path) + validateNlri(nlri bgp.AddrPrefixInterface) +} + +type TableDefault struct { + ROUTE_FAMILY RouteFamily + destinations map[string]Destination + coreService *CoreService + //need SignalBus +} + +func NewTableDefault(scope_id, coreService *CoreService) *TableDefault { + table := &TableDefault{} + table.ROUTE_FAMILY = RF_IPv4_UC + table.destinations = make(map[string]Destination) + table.coreService = coreService + return table + +} + +func (td *TableDefault) getRoutefamily() RouteFamily { + return td.ROUTE_FAMILY +} + +func (td *TableDefault) getCoreService() *CoreService { + return td.coreService +} + +//Creates destination +//Implements interface +func (td *TableDefault) createDest(nlri *bgp.NLRInfo) Destination { + //return NewDestination(td, nlri) + logger.Error("CreateDest NotImplementedError") + return nil +} + +func insert(table Table, path Path) Destination { + var dest Destination + + table.validatePath(path) + table.validateNlri(path.getNlri()) + dest = getOrCreateDest(table, path.getNlri()) + + if path.isWithdraw() { + // withdraw insert + dest.addWithdraw(path) + } else { + // path insert + dest.addNewPath(path) + } + return dest +} +func insertSentRoute(table Table, sentRoute *SentRoute) { + pd := sentRoute.path.(*PathDefault) + table.validatePath(pd) + dest := getOrCreateDest(table, pd.getNlri()) + dest.(*DestinationDefault).addSentRoute(sentRoute) +} + +//"Remove old paths from whose source is `peer` +func (td *TableDefault) cleanupPathsForPeer(peer *Peer) { + for _, dest := range td.destinations { + dd := dest.(*DestinationDefault) + pathsDeleted := dd.removeOldPathsFromSource(peer) + hadSent := dd.removeSentRoute(peer) + if hadSent { + logger.Errorf("Cleaning paths from table %s for peer %s.", td, peer) + } + if pathsDeleted != nil { + //need _signal_bus.dest_changed(dest) + } + } +} + +/* +//Cleans table of any path that do not have any RT in common with interested_rts +// Commented out because it is a VPN-related processing +func (td *TableDefault) cleanUninterestingPaths(interested_rts) int { + uninterestingDestCount = 0 + for _, dest := range td.destinations { + addedWithdraw :=dest.withdrawUnintrestingPaths(interested_rts) + if addedWithdraw{ + //need _signal_bus.dest_changed(dest) + uninterestingDestCount += 1 + } + } + return uninterestingDestCount + // need content +} +*/ + +func deleteDestByNlri(table Table, nlri *bgp.NLRInfo) Destination { + table.validateNlri(nlri) + destinations := table.getDestinations() + dest := destinations[table.tableKey(nlri).String()] + if dest != nil { + delete(destinations, table.tableKey(nlri).String()) + } + return dest +} + +func deleteDest(table Table, dest Destination) { + destinations := table.getDestinations() + delete(destinations, table.tableKey(dest.getNlri()).String()) +} + +func (td *TableDefault) validatePath(path Path) { + if path == nil || path.getRouteFamily() != td.ROUTE_FAMILY { + logger.Errorf("Invalid path. Expected instance of %s route family path, got %s.", td.ROUTE_FAMILY, path) + } +} +func (td *TableDefault) validateNlri(nlri bgp.AddrPrefixInterface) { + if nlri == nil { + logger.Error("Invalid Vpnv4 prefix given.") + } +} + +func getOrCreateDest(table Table, nlri bgp.AddrPrefixInterface) Destination { + logger.Debugf("Table type : %s", reflect.TypeOf(table)) + tableKey := table.tableKey(nlri) + dest := table.getDestination(tableKey.String()) + // If destination for given prefix does not exist we create it. + if dest == nil { + logger.Debugf("dest with key %s is not found", tableKey.String()) + dest = table.createDest(nlri) + table.setDestination(tableKey.String(), dest) + } + return dest +} + +func (td *TableDefault) getDestinations() map[string]Destination { + return td.destinations +} +func (td *TableDefault) setDestinations(destinations map[string]Destination) { + td.destinations = destinations +} +func (td *TableDefault) getDestination(key string) Destination { + dest, ok := td.destinations[key] + if ok { + return dest + } else { + return nil + } +} + +func (td *TableDefault) setDestination(key string, dest Destination) { + td.destinations[key] = dest +} + +//Implements interface +func (td *TableDefault) tableKey(nlri bgp.AddrPrefixInterface) net.IP { + //need Inheritance over ride + //return &nlri.IPAddrPrefix.IPAddrPrefixDefault.Prefix + logger.Error("CreateDest NotImplementedError") + return nil +} + +/* +* Definition of inherited Table interface + */ + +type IPv4Table struct { + *TableDefault + //need structure +} + +func NewIPv4Table(scope_id, coreService *CoreService) *IPv4Table { + ipv4Table := &IPv4Table{} + ipv4Table.TableDefault = NewTableDefault(scope_id, coreService) + ipv4Table.TableDefault.ROUTE_FAMILY = RF_IPv4_UC + //need Processing + return ipv4Table +} + +//Creates destination +//Implements interface +func (ipv4t *IPv4Table) createDest(nlri bgp.AddrPrefixInterface) Destination { + return NewIPv4Destination(nlri) +} + +//make tablekey +//Implements interface +func (ipv4t *IPv4Table) tableKey(nlri bgp.AddrPrefixInterface) net.IP { + //addrPrefix := nlri.(*bgp.IPAddrPrefix) + + var ip net.IP + switch p := nlri.(type) { + case *bgp.NLRInfo: + ip = p.IPAddrPrefix.IPAddrPrefixDefault.Prefix + case *bgp.WithdrawnRoute: + ip = p.IPAddrPrefix.IPAddrPrefixDefault.Prefix + } + return ip +} + +type IPv6Table struct { + *TableDefault + //need structure +} + +func NewIPv6Table(scope_id, coreService *CoreService) *IPv6Table { + ipv6Table := &IPv6Table{} + ipv6Table.TableDefault = NewTableDefault(scope_id, coreService) + ipv6Table.TableDefault.ROUTE_FAMILY = RF_IPv6_UC + //need Processing + return ipv6Table +} + +//Creates destination +//Implements interface +func (ipv6t *IPv6Table) createDest(nlri bgp.AddrPrefixInterface) Destination { + return Destination(NewIPv6Destination(nlri)) +} + +//make tablekey +//Implements interface +func (ipv6t *IPv6Table) tableKey(nlri bgp.AddrPrefixInterface) net.IP { + + addrPrefix := nlri.(*bgp.IPv6AddrPrefix) + return addrPrefix.IPAddrPrefixDefault.Prefix + +} diff --git a/table/table_manager.go b/table/table_manager.go new file mode 100644 index 00000000..448776a2 --- /dev/null +++ b/table/table_manager.go @@ -0,0 +1,424 @@ +// 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 table + +import ( + log "github.com/Sirupsen/logrus" + "github.com/osrg/gobgp/packet" + "net" + "os" + "reflect" + "time" +) + +var logger *log.Logger = &log.Logger{ + Out: os.Stderr, + Formatter: new(log.JSONFormatter), + Hooks: make(map[log.Level][]log.Hook), + Level: log.InfoLevel, +} + +type PeerCounterName string + +const ( + RECV_PREFIXES PeerCounterName = "recv_prefixes" + RECV_UPDATES PeerCounterName = "recv_updates" + SENT_UPDATES PeerCounterName = "sent_updates" + RECV_NOTIFICATION PeerCounterName = "recv_notification" + SENT_NOTIFICATION PeerCounterName = "sent_notification" + SENT_REFRESH PeerCounterName = "sent_refresh" + RECV_REFRESH PeerCounterName = "recv_refresh" + FSM_ESTB_TRANSITIONS PeerCounterName = "fms_established_transitions" +) + +type RouteFamily int + +const ( + RF_IPv4_UC RouteFamily = bgp.RF_IPv4_UC + RF_IPv6_UC RouteFamily = bgp.RF_IPv6_UC + RF_IPv4_VPN RouteFamily = bgp.RF_IPv4_VPN + RF_IPv6_VPN RouteFamily = bgp.RF_IPv6_VPN + RF_IPv4_MPLS RouteFamily = bgp.RF_IPv4_MPLS + RF_IPv6_MPLS RouteFamily = bgp.RF_IPv6_MPLS + RF_RTC_UC RouteFamily = bgp.RF_RTC_UC +) + +func (rf RouteFamily) String() string { + switch rf { + case RF_IPv4_UC: + return "RF_IPv4_UC" + case RF_IPv6_UC: + return "RF_IPv6_UC" + case RF_IPv4_VPN: + return "RF_IPv4_VPN" + case RF_IPv6_VPN: + return "RF_IPv6_VPN" + case RF_IPv4_MPLS: + return "RF_IPv4_MPLS" + case RF_IPv6_MPLS: + return "RF_IPv6_MPLS" + case RF_RTC_UC: + return "RF_RTC_UC" + default: + return "Unknown" + } +} + +type AttributeType int + +const ( + BGP_ATTR_TYPE_ORIGIN AttributeType = bgp.BGP_ATTR_TYPE_ORIGIN + BGP_ATTR_TYPE_AS_PATH AttributeType = bgp.BGP_ATTR_TYPE_AS_PATH + BGP_ATTR_TYPE_NEXT_HOP AttributeType = bgp.BGP_ATTR_TYPE_NEXT_HOP + BGP_ATTR_TYPE_MULTI_EXIT_DISC AttributeType = bgp.BGP_ATTR_TYPE_MULTI_EXIT_DISC + BGP_ATTR_TYPE_LOCAL_PREF AttributeType = bgp.BGP_ATTR_TYPE_LOCAL_PREF + BGP_ATTR_TYPE_ATOMIC_AGGREGATE AttributeType = bgp.BGP_ATTR_TYPE_ATOMIC_AGGREGATE + BGP_ATTR_TYPE_AGGREGATOR AttributeType = bgp.BGP_ATTR_TYPE_AGGREGATOR + BGP_ATTR_TYPE_COMMUNITIES AttributeType = bgp.BGP_ATTR_TYPE_COMMUNITIES + BGP_ATTR_TYPE_ORIGINATOR_ID AttributeType = bgp.BGP_ATTR_TYPE_ORIGINATOR_ID + BGP_ATTR_TYPE_CLUSTER_LIST AttributeType = bgp.BGP_ATTR_TYPE_CLUSTER_LIST + BGP_ATTR_TYPE_MP_REACH_NLRI AttributeType = bgp.BGP_ATTR_TYPE_MP_REACH_NLRI + BGP_ATTR_TYPE_MP_UNREACH_NLRI AttributeType = bgp.BGP_ATTR_TYPE_MP_UNREACH_NLRI + BGP_ATTR_TYPE_EXTENDED_COMMUNITIES AttributeType = bgp.BGP_ATTR_TYPE_EXTENDED_COMMUNITIES + BGP_ATTR_TYPE_AS4_PATH AttributeType = bgp.BGP_ATTR_TYPE_AS4_PATH + BGP_ATTR_TYPE_AS4_AGGREGATOR AttributeType = bgp.BGP_ATTR_TYPE_AS4_AGGREGATOR +) + +func (attr AttributeType) String() string { + switch attr { + case BGP_ATTR_TYPE_ORIGIN: + return "BGP_ATTR_TYPE_ORIGIN" + case BGP_ATTR_TYPE_AS_PATH: + return "BGP_ATTR_TYPE_AS_PATH" + case BGP_ATTR_TYPE_NEXT_HOP: + return "BGP_ATTR_TYPE_NEXT_HOP" + case BGP_ATTR_TYPE_MULTI_EXIT_DISC: + return "BGP_ATTR_TYPE_MULTI_EXIT_DISC" + case BGP_ATTR_TYPE_LOCAL_PREF: + return "BGP_ATTR_TYPE_LOCAL_PREF" + case BGP_ATTR_TYPE_ATOMIC_AGGREGATE: + return "BGP_ATTR_TYPE_ATOMIC_AGGREGATE" + case BGP_ATTR_TYPE_AGGREGATOR: + return "BGP_ATTR_TYPE_AGGREGATOR" + case BGP_ATTR_TYPE_COMMUNITIES: + return "BGP_ATTR_TYPE_COMMUNITIES" + case BGP_ATTR_TYPE_ORIGINATOR_ID: + return "BGP_ATTR_TYPE_ORIGINATOR_ID" + case BGP_ATTR_TYPE_CLUSTER_LIST: + return "BGP_ATTR_TYPE_CLUSTER_LIST" + case BGP_ATTR_TYPE_MP_REACH_NLRI: + return "BGP_ATTR_TYPE_MP_REACH_NLRI" + case BGP_ATTR_TYPE_MP_UNREACH_NLRI: + return "BGP_ATTR_TYPE_MP_UNREACH_NLRI" + case BGP_ATTR_TYPE_EXTENDED_COMMUNITIES: + return "BGP_ATTR_TYPE_EXTENDED_COMMUNITIES" + case BGP_ATTR_TYPE_AS4_PATH: + return "BGP_ATTR_TYPE_AS4_PATH" + case BGP_ATTR_TYPE_AS4_AGGREGATOR: + return "BGP_ATTR_TYPE_AS4_AGGREGATOR" + default: + return "Unknown" + } +} + +type TableManager struct { + Tables map[RouteFamily]Table + adjInLocalRib map[string]*ReceivedRoute + processMessages chan *ProcessMessage + Counter map[PeerCounterName]int + localAsn uint32 +} + +type ProcessMessage struct { + innerMessage *bgp.BGPMessage + fromPeer *Peer +} + +func NewTableManager() *TableManager { + t := &TableManager{} + t.Tables = make(map[RouteFamily]Table) + t.Tables[RF_IPv4_UC] = NewIPv4Table(0, nil) + t.Tables[RF_IPv6_UC] = NewIPv6Table(0, nil) + + t.processMessages = make(chan *ProcessMessage, 10) + // initialize prefix counter + t.Counter = make(map[PeerCounterName]int) + t.Counter[RECV_PREFIXES] = 0 + + return t +} + +func setLogger(loggerInstance *log.Logger) { + logger = loggerInstance +} + +func (manager *TableManager) incrCounter(name PeerCounterName, step int) { + val := manager.Counter[name] + val += step + manager.Counter[name] = val +} + +// create destination list from nlri +func (manager *TableManager) handleNlri(p *ProcessMessage) ([]Destination, error) { + + updateMsg := p.innerMessage.Body.(*bgp.BGPUpdate) + nlriList := updateMsg.NLRI + pathAttributes := updateMsg.PathAttributes + + destList := make([]Destination, 0) + for _, nlri_info := range nlriList { + // create Path object + path := CreatePath(p.fromPeer, &nlri_info, pathAttributes, false) + // TODO process filter + + rf := path.getRouteFamily() + // push Path into table + destination := insert(manager.Tables[rf], path) + + destList = append(destList, destination) + manager.incrCounter(RECV_PREFIXES, len(nlriList)) + // TODO handle adj-in-loc-rib + // rr := NewReceivedRoute(path, p.fromPeer, false) + // manager.adjInLocalRib[p.fromPeer.String()] = rr + // manager.adjInChanged <- rr + } + + logger.Debugf("destinationList contains %d destinations from nlri", len(destList)) + + return destList, nil +} + +// create destination list from withdrawn routes +func (manager *TableManager) handleWithdraw(p *ProcessMessage) ([]Destination, error) { + + updateMsg := p.innerMessage.Body.(*bgp.BGPUpdate) + pathAttributes := updateMsg.PathAttributes + withdrawnRoutes := updateMsg.WithdrawnRoutes + + wDestList := make([]Destination, 0) + + // process withdraw path + for _, nlriWithdraw := range withdrawnRoutes { + // create withdrawn Path object + path := CreatePath(p.fromPeer, &nlriWithdraw, pathAttributes, true) + rf := path.getRouteFamily() + // push Path into table + destination := insert(manager.Tables[rf], path) + wDestList = append(wDestList, destination) + } + + logger.Debugf("destinationList contains %d withdrawn destinations", len(wDestList)) + return wDestList, nil +} + +// create destination list from nlri +func (manager *TableManager) handleMPReachNlri(p *ProcessMessage) ([]Destination, error) { + + updateMsg := p.innerMessage.Body.(*bgp.BGPUpdate) + pathAttributes := updateMsg.PathAttributes + attrList := []*bgp.PathAttributeMpReachNLRI{} + + for _, attr := range pathAttributes { + logger.Debugf("attr type: %s", reflect.TypeOf(attr)) + switch a := attr.(type) { + case *bgp.PathAttributeMpReachNLRI: + attrList = append(attrList, a) + } + } + + destList := make([]Destination, 0) + for _, mp := range attrList { + nlri_info := mp.Value + + for _, nlri := range nlri_info { + path := CreatePath(p.fromPeer, nlri, pathAttributes, false) + // TODO process filter + + rf := path.getRouteFamily() + // push Path into table + destination := insert(manager.Tables[rf], path) + + destList = append(destList, destination) + manager.incrCounter(RECV_PREFIXES, len(nlri_info)) + // TODO handle adj-in-loc-rib + // rr := NewReceivedRoute(path, p.fromPeer, false) + // manager.adjInLocalRib[p.fromPeer.String()] = rr + // manager.adjInChanged <- rr + } + } + logger.Debugf("destinationList contains %d destinations from MpReachNLRI", len(destList)) + + return destList, nil +} + +// create destination list from nlri +func (manager *TableManager) handleMPUNReachNlri(p *ProcessMessage) ([]Destination, error) { + + updateMsg := p.innerMessage.Body.(*bgp.BGPUpdate) + pathAttributes := updateMsg.PathAttributes + attrList := []*bgp.PathAttributeMpUnreachNLRI{} + + for _, attr := range pathAttributes { + switch a := attr.(type) { + case *bgp.PathAttributeMpUnreachNLRI: + attrList = append(attrList, a) + } + } + + destList := make([]Destination, 0) + for _, mp := range attrList { + nlri_info := mp.Value + + for _, nlri := range nlri_info { + path := CreatePath(p.fromPeer, nlri, pathAttributes, true) + // TODO process filter + + rf := path.getRouteFamily() + // push Path into table + destination := insert(manager.Tables[rf], path) + + destList = append(destList, destination) + manager.incrCounter(RECV_PREFIXES, len(nlri_info)) + } + } + logger.Debugf("destinationList contains %d destinations from MpUnreachNLRI", len(destList)) + return destList, nil +} + +// process BGPUpdate message +// this function processes only BGPUpdate +func (manager *TableManager) ProcessUpdate(fromPeer *Peer, message *bgp.BGPMessage) ([]Path, []Path, error) { + + var bestPaths []Path = make([]Path, 0) + var withdrawPaths []Path = make([]Path, 0) + + // check msg's type if it's BGPUpdate + body := message.Body + switch body.(type) { + case *bgp.BGPUpdate: + + msg := &ProcessMessage{ + innerMessage: message, + fromPeer: fromPeer, + } + + // get destination list + destList, err := manager.handleNlri(msg) + if err != nil { + logger.Error(err) + return nil, nil, err + } + + wDestList, err := manager.handleWithdraw(msg) + if err != nil { + logger.Error(err) + return nil, nil, err + } + + mpreachDestList, err := manager.handleMPReachNlri(msg) + if err != nil { + logger.Error(err) + return nil, nil, err + } + + mpunreachDestList, err := manager.handleMPUNReachNlri(msg) + if err != nil { + logger.Error(err) + return nil, nil, err + } + // merge destList and wDestList + destinationList := append(destList, wDestList...) + destinationList = append(destinationList, mpreachDestList...) + destinationList = append(destinationList, mpunreachDestList...) + + // check best path changed + if destinationList != nil { + for _, destination := range destinationList { + // compute best path + logger.Infof("Processing destination: %s", destination.String()) + newBestPath, reason, err := destination.Calculate(manager.localAsn) + + logger.Debugf("new best path: %s, reason=%s", newBestPath, reason) + logger.Infof("new best path: NLRI: %s, next_hop=%s, reason=%s", newBestPath.getPrefix().String(), + newBestPath.getNexthop().String(), reason) + if err != nil { + logger.Error(err) + continue + } + + destination.setBestPathReason(reason) + currentBestPath := destination.getBestPath() + + if newBestPath != nil && currentBestPath == newBestPath { + // best path is not changed + logger.Debug("best path is not changed") + continue + } + + if newBestPath == nil { + + logger.Debug("new best path is nil") + + if len(destination.getKnownPathList()) == 0 { + // create withdraw path + if currentBestPath != nil { + withdrawPaths = append(withdrawPaths, currentBestPath) + } + destination.setBestPath(nil) + } else { + panic("known path list is not empty") + } + } else { + logger.Debug("best path : ", newBestPath.String()) + bestPaths = append(bestPaths, newBestPath) + destination.setBestPath(newBestPath) + } + + if len(destination.getKnownPathList()) == 0 && destination.getBestPath() == nil { + rf := destination.getRouteFamily() + t := manager.Tables[rf] + deleteDest(t, destination) + } + } + } + default: + logger.Warn("message is not BGPUpdate") + } + + return bestPaths, withdrawPaths, nil +} + +type ReceivedRoute struct { + path Path + fromPeer *net.IP + filtered bool + timestamp time.Time +} + +func (rr *ReceivedRoute) String() string { + return rr.path.(*PathDefault).getPrefix().String() +} + +func NewReceivedRoute(path Path, peer *net.IP, filtered bool) *ReceivedRoute { + + rroute := &ReceivedRoute{ + path: path, + fromPeer: peer, + filtered: filtered, + timestamp: time.Now(), + } + return rroute +} diff --git a/table/table_manager_test.go b/table/table_manager_test.go new file mode 100644 index 00000000..8b1d7e2c --- /dev/null +++ b/table/table_manager_test.go @@ -0,0 +1,1482 @@ +// 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 table + +import ( + _ "fmt" + log "github.com/Sirupsen/logrus" + "github.com/osrg/gobgp/packet" + "github.com/stretchr/testify/assert" + "net" + "os" + "reflect" + "testing" +) + +func getLogger() *log.Logger { + var l *log.Logger = &log.Logger{ + Out: os.Stderr, + Formatter: new(log.JSONFormatter), + Hooks: make(map[log.Level][]log.Hook), + Level: log.InfoLevel, + } + return l +} + +func peerR1() *Peer { + proto := &BgpProtocol{} + proto.sentOpenMsg = bgp.NewBGPOpenMessage(65000, 300, "10.0.0.1", nil).Body.(*bgp.BGPOpen) + proto.recvOpenMsg = bgp.NewBGPOpenMessage(65000, 300, "10.0.0.3", nil).Body.(*bgp.BGPOpen) + + peer := &Peer{ + VersionNum: 4, + RemoteAs: 65000, + protocol: proto, + } + return peer +} + +func peerR2() *Peer { + peer := &Peer{ + VersionNum: 4, + RemoteAs: 65100, + } + return peer +} + +func peerR3() *Peer { + proto := &BgpProtocol{} + proto.sentOpenMsg = bgp.NewBGPOpenMessage(65000, 300, "10.0.0.1", nil).Body.(*bgp.BGPOpen) + proto.recvOpenMsg = bgp.NewBGPOpenMessage(65000, 300, "10.0.0.2", nil).Body.(*bgp.BGPOpen) + + peer := &Peer{ + VersionNum: 4, + RemoteAs: 65000, + protocol: proto, + } + return peer +} + +// test best path calculation and check the result path is from R1 +func TestProcessBGPUpdate_0_select_onlypath_ipv4(t *testing.T) { + + tm := NewTableManager() + setLogger(getLogger()) + + bgpMessage := update_fromR1() + peer := peerR1() + pList, wList, err := tm.ProcessUpdate(peer, bgpMessage) + assert.Equal(t, len(pList), 1, "pList length should be 1") + assert.Equal(t, len(wList), 0, "wList length should be 0") + assert.NoError(t, err, "err should be nil") + + // check type + path := pList[0] + expectedType := "*table.IPv4Path" + assert.Equal(t, reflect.TypeOf(path).String(), expectedType, "best path should be *table.IPv4Path") + + // check PathAttribute + pathAttributes := bgpMessage.Body.(*bgp.BGPUpdate).PathAttributes + expectedOrigin := pathAttributes[0] + pathOrigin := path.getPathAttribute(bgp.BGP_ATTR_TYPE_ORIGIN).(*bgp.PathAttributeOrigin) + assert.Equal(t, pathOrigin, expectedOrigin, "PathAttributeOrigin should be ", expectedOrigin) + + expectedAsPath := pathAttributes[1] + pathAspath := path.getPathAttribute(bgp.BGP_ATTR_TYPE_AS_PATH).(*bgp.PathAttributeAsPath) + assert.Equal(t, pathAspath, expectedAsPath, "PathAttributeAsPath should be ", expectedAsPath) + + expectedNexthopAttr := pathAttributes[2] + pathNexthop := path.getPathAttribute(bgp.BGP_ATTR_TYPE_NEXT_HOP).(*bgp.PathAttributeNextHop) + assert.Equal(t, pathNexthop, expectedNexthopAttr, "PathAttributeNextHop should be ", expectedNexthopAttr) + + expectedMed := pathAttributes[3] + pathMed := path.getPathAttribute(bgp.BGP_ATTR_TYPE_MULTI_EXIT_DISC).(*bgp.PathAttributeMultiExitDisc) + assert.Equal(t, expectedMed, pathMed, "PathAttributeMed should be ", expectedMed) + + // check PathAttribute length + assert.Equal(t, 4, path.getPathAttributeMap().Len(), "PathAttribute length should be ", 4) + + // check destination + expectedPrefix := "10.10.10.0" + assert.Equal(t, expectedPrefix, path.getPrefix().String(), "prefix should be ", expectedPrefix) + // check nexthop + expectedNexthop := "192.168.50.1" + assert.Equal(t, expectedNexthop, path.getNexthop().String(), "nexthop should be ", expectedNexthop) + +} + +// test best path calculation and check the result path is from R1 +func TestProcessBGPUpdate_0_select_onlypath_ipv6(t *testing.T) { + + tm := NewTableManager() + setLogger(getLogger()) + + bgpMessage := update_fromR1_ipv6() + peer := peerR1() + pList, wList, err := tm.ProcessUpdate(peer, bgpMessage) + assert.Equal(t, 1, len(pList)) + assert.Equal(t, 0, len(wList)) + assert.NoError(t, err) + + // check type + path := pList[0] + expectedType := "*table.IPv6Path" + assert.Equal(t, expectedType, reflect.TypeOf(path).String(), "best path should be *table.IPv6Path") + + // check PathAttribute + pathAttributes := bgpMessage.Body.(*bgp.BGPUpdate).PathAttributes + + expectedNexthopAttr := pathAttributes[0] + pathNexthop := path.getPathAttribute(bgp.BGP_ATTR_TYPE_MP_REACH_NLRI).(*bgp.PathAttributeMpReachNLRI) + assert.Equal(t, pathNexthop, expectedNexthopAttr, "PathAttributeNextHop should be ", expectedNexthopAttr) + + expectedOrigin := pathAttributes[1] + pathOrigin := path.getPathAttribute(bgp.BGP_ATTR_TYPE_ORIGIN).(*bgp.PathAttributeOrigin) + assert.Equal(t, pathOrigin, expectedOrigin, "PathAttributeOrigin should be ", expectedOrigin) + + expectedAsPath := pathAttributes[2] + pathAspath := path.getPathAttribute(bgp.BGP_ATTR_TYPE_AS_PATH).(*bgp.PathAttributeAsPath) + assert.Equal(t, pathAspath, expectedAsPath, "PathAttributeAsPath should be ", expectedAsPath) + + expectedMed := pathAttributes[3] + pathMed := path.getPathAttribute(bgp.BGP_ATTR_TYPE_MULTI_EXIT_DISC).(*bgp.PathAttributeMultiExitDisc) + assert.Equal(t, expectedMed, pathMed, "PathAttributeMed should be ", expectedMed) + + // check PathAttribute length + assert.Equal(t, 4, path.getPathAttributeMap().Len(), "PathAttribute length should be ", 4) + + // check destination + expectedPrefix := "2001:123:123:1::" + assert.Equal(t, expectedPrefix, path.getPrefix().String(), "prefix should be ", expectedPrefix) + // check nexthop + expectedNexthop := "2001::192:168:50:1" + assert.Equal(t, expectedNexthop, path.getNexthop().String(), "nexthop should be ", expectedNexthop) + +} + +// test: compare localpref +func TestProcessBGPUpdate_1_select_high_localpref_ipv4(t *testing.T) { + + tm := NewTableManager() + var pList, wList []Path + var err error + + // low localpref message + origin1 := bgp.NewPathAttributeOrigin(0) + aspath1 := createAsPathAttribute([]uint16{65000}) + nexthop1 := bgp.NewPathAttributeNextHop("192.168.50.1") + med1 := bgp.NewPathAttributeMultiExitDisc(0) + localpref1 := bgp.NewPathAttributeLocalPref(100) + + pathAttributes1 := []bgp.PathAttributeInterface{ + origin1, aspath1, nexthop1, med1, localpref1, + } + nlri1 := []bgp.NLRInfo{*bgp.NewNLRInfo(24, "10.10.10.0")} + withdrawnRoutes1 := []bgp.WithdrawnRoute{} + bgpMessage1 := bgp.NewBGPUpdateMessage(withdrawnRoutes1, pathAttributes1, nlri1) + + // high localpref message + origin2 := bgp.NewPathAttributeOrigin(0) + aspath2 := createAsPathAttribute([]uint16{65100, 65000}) + nexthop2 := bgp.NewPathAttributeNextHop("192.168.50.1") + med2 := bgp.NewPathAttributeMultiExitDisc(100) + localpref2 := bgp.NewPathAttributeLocalPref(200) + + pathAttributes2 := []bgp.PathAttributeInterface{ + origin2, aspath2, nexthop2, med2, localpref2, + } + nlri2 := []bgp.NLRInfo{*bgp.NewNLRInfo(24, "10.10.10.0")} + withdrawnRoutes2 := []bgp.WithdrawnRoute{} + bgpMessage2 := bgp.NewBGPUpdateMessage(withdrawnRoutes2, pathAttributes2, nlri2) + + peer1 := peerR1() + pList, wList, err = tm.ProcessUpdate(peer1, bgpMessage1) + assert.Equal(t, 1, len(pList), "pList length should be 1") + assert.Equal(t, 0, len(wList), "wList length should be 0") + assert.NoError(t, err, "err should be nil") + + peer2 := peerR2() + pList, wList, err = tm.ProcessUpdate(peer2, bgpMessage2) + assert.Equal(t, 1, len(pList), "pList length should be 1") + assert.Equal(t, 0, len(wList), "wList length should be 0") + assert.NoError(t, err, "err should be nil") + + // check type + path := pList[0] + expectedType := "*table.IPv4Path" + assert.Equal(t, reflect.TypeOf(path).String(), expectedType, "best path should be *table.IPv4Path") + + // check PathAttribute + pathAttributes := bgpMessage2.Body.(*bgp.BGPUpdate).PathAttributes + expectedOrigin := pathAttributes[0] + pathOrigin := path.getPathAttribute(bgp.BGP_ATTR_TYPE_ORIGIN).(*bgp.PathAttributeOrigin) + assert.Equal(t, pathOrigin, expectedOrigin, "PathAttributeOrigin should be ", expectedOrigin) + + expectedAsPath := pathAttributes[1] + pathAspath := path.getPathAttribute(bgp.BGP_ATTR_TYPE_AS_PATH).(*bgp.PathAttributeAsPath) + assert.Equal(t, pathAspath, expectedAsPath, "PathAttributeAsPath should be ", expectedAsPath) + + expectedNexthopAttr := pathAttributes[2] + pathNexthop := path.getPathAttribute(bgp.BGP_ATTR_TYPE_NEXT_HOP).(*bgp.PathAttributeNextHop) + assert.Equal(t, pathNexthop, expectedNexthopAttr, "PathAttributeNextHop should be ", expectedNexthopAttr) + + expectedMed := pathAttributes[3] + pathMed := path.getPathAttribute(bgp.BGP_ATTR_TYPE_MULTI_EXIT_DISC).(*bgp.PathAttributeMultiExitDisc) + assert.Equal(t, expectedMed, pathMed, "PathAttributeMed should be ", expectedMed) + + // check PathAttribute length + assert.Equal(t, len(pathAttributes2), path.getPathAttributeMap().Len(), "PathAttribute length should be ", 4) + + // check destination + expectedPrefix := "10.10.10.0" + assert.Equal(t, expectedPrefix, path.getPrefix().String(), "prefix should be ", expectedPrefix) + // check nexthop + expectedNexthop := "192.168.50.1" + assert.Equal(t, expectedNexthop, path.getNexthop().String(), "nexthop should be ", expectedNexthop) + +} + +func TestProcessBGPUpdate_1_select_high_localpref_ipv6(t *testing.T) { + + tm := NewTableManager() + var pList, wList []Path + var err error + + origin1 := bgp.NewPathAttributeOrigin(0) + aspath1 := createAsPathAttribute([]uint16{65000}) + mp_reach1 := createMpReach("2001::192:168:50:1", "2001:123:123:1::", 64) + med1 := bgp.NewPathAttributeMultiExitDisc(100) + localpref1 := bgp.NewPathAttributeLocalPref(100) + + pathAttributes1 := []bgp.PathAttributeInterface{ + mp_reach1, origin1, aspath1, med1, localpref1, + } + + nlri1 := []bgp.NLRInfo{} + withdrawnRoutes1 := []bgp.WithdrawnRoute{} + bgpMessage1 := bgp.NewBGPUpdateMessage(withdrawnRoutes1, pathAttributes1, nlri1) + + origin2 := bgp.NewPathAttributeOrigin(0) + aspath2 := createAsPathAttribute([]uint16{65100, 65000}) + mp_reach2 := createMpReach("2001::192:168:100:1", "2001:123:123:1::", 64) + med2 := bgp.NewPathAttributeMultiExitDisc(100) + localpref2 := bgp.NewPathAttributeLocalPref(200) + + pathAttributes2 := []bgp.PathAttributeInterface{ + mp_reach2, origin2, aspath2, med2, localpref2, + } + + nlri2 := []bgp.NLRInfo{} + withdrawnRoutes2 := []bgp.WithdrawnRoute{} + bgpMessage2 := bgp.NewBGPUpdateMessage(withdrawnRoutes2, pathAttributes2, nlri2) + + peer1 := peerR1() + pList, wList, err = tm.ProcessUpdate(peer1, bgpMessage1) + assert.Equal(t, 1, len(pList)) + assert.Equal(t, 0, len(wList)) + assert.NoError(t, err, "err should be nil") + + peer2 := peerR2() + pList, wList, err = tm.ProcessUpdate(peer2, bgpMessage2) + assert.Equal(t, 1, len(pList)) + assert.Equal(t, 0, len(wList)) + assert.NoError(t, err, "err should be nil") + + // check type + path := pList[0] + expectedType := "*table.IPv6Path" + assert.Equal(t, reflect.TypeOf(path).String(), expectedType) + + // check PathAttribute + pathAttributes := bgpMessage2.Body.(*bgp.BGPUpdate).PathAttributes + + expectedNexthopAttr := pathAttributes[0] + pathNexthop := path.getPathAttribute(bgp.BGP_ATTR_TYPE_MP_REACH_NLRI).(*bgp.PathAttributeMpReachNLRI) + assert.Equal(t, pathNexthop, expectedNexthopAttr) + + expectedOrigin := pathAttributes[1] + pathOrigin := path.getPathAttribute(bgp.BGP_ATTR_TYPE_ORIGIN).(*bgp.PathAttributeOrigin) + assert.Equal(t, pathOrigin, expectedOrigin) + + expectedAsPath := pathAttributes[2] + pathAspath := path.getPathAttribute(bgp.BGP_ATTR_TYPE_AS_PATH).(*bgp.PathAttributeAsPath) + assert.Equal(t, pathAspath, expectedAsPath) + + expectedMed := pathAttributes[3] + pathMed := path.getPathAttribute(bgp.BGP_ATTR_TYPE_MULTI_EXIT_DISC).(*bgp.PathAttributeMultiExitDisc) + assert.Equal(t, expectedMed, pathMed) + + // check PathAttribute length + assert.Equal(t, 5, path.getPathAttributeMap().Len()) + + // check destination + expectedPrefix := "2001:123:123:1::" + assert.Equal(t, expectedPrefix, path.getPrefix().String()) + // check nexthop + expectedNexthop := "2001::192:168:100:1" + assert.Equal(t, expectedNexthop, path.getNexthop().String()) + +} + +// test: compare localOrigin +func TestProcessBGPUpdate_2_select_local_origin_ipv4(t *testing.T) { + + tm := NewTableManager() + var pList, wList []Path + var err error + + // low localpref message + origin1 := bgp.NewPathAttributeOrigin(0) + aspath1 := createAsPathAttribute([]uint16{65000}) + nexthop1 := bgp.NewPathAttributeNextHop("192.168.50.1") + med1 := bgp.NewPathAttributeMultiExitDisc(0) + localpref1 := bgp.NewPathAttributeLocalPref(100) + + pathAttributes1 := []bgp.PathAttributeInterface{ + origin1, aspath1, nexthop1, med1, localpref1, + } + nlri1 := []bgp.NLRInfo{*bgp.NewNLRInfo(24, "10.10.10.0")} + withdrawnRoutes1 := []bgp.WithdrawnRoute{} + bgpMessage1 := bgp.NewBGPUpdateMessage(withdrawnRoutes1, pathAttributes1, nlri1) + + // high localpref message + origin2 := bgp.NewPathAttributeOrigin(0) + aspath2 := createAsPathAttribute([]uint16{}) + nexthop2 := bgp.NewPathAttributeNextHop("0.0.0.0") + med2 := bgp.NewPathAttributeMultiExitDisc(100) + localpref2 := bgp.NewPathAttributeLocalPref(100) + + pathAttributes2 := []bgp.PathAttributeInterface{ + origin2, aspath2, nexthop2, med2, localpref2, + } + nlri2 := []bgp.NLRInfo{*bgp.NewNLRInfo(24, "10.10.10.0")} + withdrawnRoutes2 := []bgp.WithdrawnRoute{} + bgpMessage2 := bgp.NewBGPUpdateMessage(withdrawnRoutes2, pathAttributes2, nlri2) + + peer1 := peerR1() + pList, wList, err = tm.ProcessUpdate(peer1, bgpMessage1) + assert.Equal(t, 1, len(pList)) + assert.Equal(t, 0, len(wList)) + assert.NoError(t, err) + + var peer2 *Peer = nil + pList, wList, err = tm.ProcessUpdate(peer2, bgpMessage2) + assert.Equal(t, 1, len(pList)) + assert.Equal(t, 0, len(wList)) + assert.NoError(t, err) + + // check type + path := pList[0] + expectedType := "*table.IPv4Path" + assert.Equal(t, reflect.TypeOf(path).String(), expectedType) + + // check PathAttribute + pathAttributes := bgpMessage2.Body.(*bgp.BGPUpdate).PathAttributes + expectedOrigin := pathAttributes[0] + pathOrigin := path.getPathAttribute(bgp.BGP_ATTR_TYPE_ORIGIN).(*bgp.PathAttributeOrigin) + assert.Equal(t, pathOrigin, expectedOrigin) + + expectedAsPath := pathAttributes[1] + pathAspath := path.getPathAttribute(bgp.BGP_ATTR_TYPE_AS_PATH).(*bgp.PathAttributeAsPath) + assert.Equal(t, pathAspath, expectedAsPath) + + expectedNexthopAttr := pathAttributes[2] + pathNexthop := path.getPathAttribute(bgp.BGP_ATTR_TYPE_NEXT_HOP).(*bgp.PathAttributeNextHop) + assert.Equal(t, pathNexthop, expectedNexthopAttr) + + expectedMed := pathAttributes[3] + pathMed := path.getPathAttribute(bgp.BGP_ATTR_TYPE_MULTI_EXIT_DISC).(*bgp.PathAttributeMultiExitDisc) + assert.Equal(t, expectedMed, pathMed) + + // check PathAttribute length + assert.Equal(t, len(pathAttributes2), path.getPathAttributeMap().Len()) + + // check destination + expectedPrefix := "10.10.10.0" + assert.Equal(t, expectedPrefix, path.getPrefix().String()) + // check nexthop + expectedNexthop := "0.0.0.0" + assert.Equal(t, expectedNexthop, path.getNexthop().String()) + +} + +func TestProcessBGPUpdate_2_select_local_origin_ipv6(t *testing.T) { + + tm := NewTableManager() + var pList, wList []Path + var err error + + origin1 := bgp.NewPathAttributeOrigin(0) + aspath1 := createAsPathAttribute([]uint16{65000}) + mp_reach1 := createMpReach("2001::192:168:50:1", "2001:123:123:1::", 64) + med1 := bgp.NewPathAttributeMultiExitDisc(100) + localpref1 := bgp.NewPathAttributeLocalPref(100) + + pathAttributes1 := []bgp.PathAttributeInterface{ + mp_reach1, origin1, aspath1, med1, localpref1, + } + + nlri1 := []bgp.NLRInfo{} + withdrawnRoutes1 := []bgp.WithdrawnRoute{} + bgpMessage1 := bgp.NewBGPUpdateMessage(withdrawnRoutes1, pathAttributes1, nlri1) + + origin2 := bgp.NewPathAttributeOrigin(0) + aspath2 := createAsPathAttribute([]uint16{}) + mp_reach2 := createMpReach("::", "2001:123:123:1::", 64) + med2 := bgp.NewPathAttributeMultiExitDisc(100) + localpref2 := bgp.NewPathAttributeLocalPref(100) + + pathAttributes2 := []bgp.PathAttributeInterface{ + mp_reach2, origin2, aspath2, med2, localpref2, + } + + nlri2 := []bgp.NLRInfo{} + withdrawnRoutes2 := []bgp.WithdrawnRoute{} + bgpMessage2 := bgp.NewBGPUpdateMessage(withdrawnRoutes2, pathAttributes2, nlri2) + + peer1 := peerR1() + pList, wList, err = tm.ProcessUpdate(peer1, bgpMessage1) + assert.Equal(t, 1, len(pList)) + assert.Equal(t, 0, len(wList)) + assert.NoError(t, err, "err should be nil") + + var peer2 *Peer = nil + pList, wList, err = tm.ProcessUpdate(peer2, bgpMessage2) + assert.Equal(t, 1, len(pList)) + assert.Equal(t, 0, len(wList)) + assert.NoError(t, err, "err should be nil") + + // check type + path := pList[0] + expectedType := "*table.IPv6Path" + assert.Equal(t, reflect.TypeOf(path).String(), expectedType) + + // check PathAttribute + pathAttributes := bgpMessage2.Body.(*bgp.BGPUpdate).PathAttributes + + expectedNexthopAttr := pathAttributes[0] + pathNexthop := path.getPathAttribute(bgp.BGP_ATTR_TYPE_MP_REACH_NLRI).(*bgp.PathAttributeMpReachNLRI) + assert.Equal(t, pathNexthop, expectedNexthopAttr) + + expectedOrigin := pathAttributes[1] + pathOrigin := path.getPathAttribute(bgp.BGP_ATTR_TYPE_ORIGIN).(*bgp.PathAttributeOrigin) + assert.Equal(t, pathOrigin, expectedOrigin) + + expectedAsPath := pathAttributes[2] + pathAspath := path.getPathAttribute(bgp.BGP_ATTR_TYPE_AS_PATH).(*bgp.PathAttributeAsPath) + assert.Equal(t, pathAspath, expectedAsPath) + + expectedMed := pathAttributes[3] + pathMed := path.getPathAttribute(bgp.BGP_ATTR_TYPE_MULTI_EXIT_DISC).(*bgp.PathAttributeMultiExitDisc) + assert.Equal(t, expectedMed, pathMed) + + // check PathAttribute length + assert.Equal(t, 5, path.getPathAttributeMap().Len()) + + // check destination + expectedPrefix := "2001:123:123:1::" + assert.Equal(t, expectedPrefix, path.getPrefix().String()) + // check nexthop + expectedNexthop := "::" + assert.Equal(t, expectedNexthop, path.getNexthop().String()) + +} + +// test: compare AS_PATH +func TestProcessBGPUpdate_3_select_aspath_ipv4(t *testing.T) { + + tm := NewTableManager() + var pList, wList []Path + var err error + + bgpMessage1 := update_fromR2viaR1() + peer1 := peerR1() + pList, wList, err = tm.ProcessUpdate(peer1, bgpMessage1) + assert.Equal(t, 1, len(pList), "pList length should be 1") + assert.Equal(t, 0, len(wList), "wList length should be 0") + assert.NoError(t, err, "err should be nil") + bgpMessage2 := update_fromR2() + peer2 := peerR2() + pList, wList, err = tm.ProcessUpdate(peer2, bgpMessage2) + assert.Equal(t, 1, len(pList), "pList length should be 1") + assert.Equal(t, 0, len(wList), "wList length should be 0") + assert.NoError(t, err, "err should be nil") + + // check type + path := pList[0] + expectedType := "*table.IPv4Path" + assert.Equal(t, reflect.TypeOf(path).String(), expectedType, "best path should be *table.IPv4Path") + + // check PathAttribute + pathAttributes := bgpMessage2.Body.(*bgp.BGPUpdate).PathAttributes + expectedOrigin := pathAttributes[0] + pathOrigin := path.getPathAttribute(bgp.BGP_ATTR_TYPE_ORIGIN).(*bgp.PathAttributeOrigin) + assert.Equal(t, pathOrigin, expectedOrigin, "PathAttributeOrigin should be ", expectedOrigin) + + expectedAsPath := pathAttributes[1] + pathAspath := path.getPathAttribute(bgp.BGP_ATTR_TYPE_AS_PATH).(*bgp.PathAttributeAsPath) + assert.Equal(t, pathAspath, expectedAsPath, "PathAttributeAsPath should be ", expectedAsPath) + + expectedNexthopAttr := pathAttributes[2] + pathNexthop := path.getPathAttribute(bgp.BGP_ATTR_TYPE_NEXT_HOP).(*bgp.PathAttributeNextHop) + assert.Equal(t, pathNexthop, expectedNexthopAttr, "PathAttributeNextHop should be ", expectedNexthopAttr) + + expectedMed := pathAttributes[3] + pathMed := path.getPathAttribute(bgp.BGP_ATTR_TYPE_MULTI_EXIT_DISC).(*bgp.PathAttributeMultiExitDisc) + assert.Equal(t, expectedMed, pathMed, "PathAttributeMed should be ", expectedMed) + + // check PathAttribute length + assert.Equal(t, 4, path.getPathAttributeMap().Len(), "PathAttribute length should be ", 4) + + // check destination + expectedPrefix := "20.20.20.0" + assert.Equal(t, expectedPrefix, path.getPrefix().String(), "prefix should be ", expectedPrefix) + // check nexthop + expectedNexthop := "192.168.100.1" + assert.Equal(t, expectedNexthop, path.getNexthop().String(), "nexthop should be ", expectedNexthop) + +} + +func TestProcessBGPUpdate_3_select_aspath_ipv6(t *testing.T) { + + tm := NewTableManager() + var pList, wList []Path + var err error + + bgpMessage1 := update_fromR2viaR1_ipv6() + peer1 := peerR1() + pList, wList, err = tm.ProcessUpdate(peer1, bgpMessage1) + assert.Equal(t, 1, len(pList)) + assert.Equal(t, 0, len(wList)) + assert.NoError(t, err, "err should be nil") + bgpMessage2 := update_fromR2_ipv6() + peer2 := peerR2() + pList, wList, err = tm.ProcessUpdate(peer2, bgpMessage2) + assert.Equal(t, 1, len(pList)) + assert.Equal(t, 0, len(wList)) + assert.NoError(t, err, "err should be nil") + + // check type + path := pList[0] + expectedType := "*table.IPv6Path" + assert.Equal(t, reflect.TypeOf(path).String(), expectedType, "best path should be *table.IPv6Path") + + // check PathAttribute + pathAttributes := bgpMessage2.Body.(*bgp.BGPUpdate).PathAttributes + + expectedNexthopAttr := pathAttributes[0] + pathNexthop := path.getPathAttribute(bgp.BGP_ATTR_TYPE_MP_REACH_NLRI).(*bgp.PathAttributeMpReachNLRI) + assert.Equal(t, pathNexthop, expectedNexthopAttr, "PathAttributeNextHop should be ", expectedNexthopAttr) + + expectedOrigin := pathAttributes[1] + pathOrigin := path.getPathAttribute(bgp.BGP_ATTR_TYPE_ORIGIN).(*bgp.PathAttributeOrigin) + assert.Equal(t, pathOrigin, expectedOrigin, "PathAttributeOrigin should be ", expectedOrigin) + + expectedAsPath := pathAttributes[2] + pathAspath := path.getPathAttribute(bgp.BGP_ATTR_TYPE_AS_PATH).(*bgp.PathAttributeAsPath) + assert.Equal(t, pathAspath, expectedAsPath, "PathAttributeAsPath should be ", expectedAsPath) + + expectedMed := pathAttributes[3] + pathMed := path.getPathAttribute(bgp.BGP_ATTR_TYPE_MULTI_EXIT_DISC).(*bgp.PathAttributeMultiExitDisc) + assert.Equal(t, expectedMed, pathMed, "PathAttributeMed should be ", expectedMed) + + // check PathAttribute length + assert.Equal(t, 4, path.getPathAttributeMap().Len(), "PathAttribute length should be ", 4) + + // check destination + expectedPrefix := "2002:223:123:1::" + assert.Equal(t, expectedPrefix, path.getPrefix().String(), "prefix should be ", expectedPrefix) + // check nexthop + expectedNexthop := "2001::192:168:100:1" + assert.Equal(t, expectedNexthop, path.getNexthop().String(), "nexthop should be ", expectedNexthop) + +} + +// test: compare Origin +func TestProcessBGPUpdate_4_select_low_origin_ipv4(t *testing.T) { + + tm := NewTableManager() + var pList, wList []Path + var err error + + // low origin message + origin1 := bgp.NewPathAttributeOrigin(1) + aspath1 := createAsPathAttribute([]uint16{65200, 65000}) + nexthop1 := bgp.NewPathAttributeNextHop("192.168.50.1") + med1 := bgp.NewPathAttributeMultiExitDisc(100) + localpref1 := bgp.NewPathAttributeLocalPref(100) + + pathAttributes1 := []bgp.PathAttributeInterface{ + origin1, aspath1, nexthop1, med1, localpref1, + } + nlri1 := []bgp.NLRInfo{*bgp.NewNLRInfo(24, "10.10.10.0")} + withdrawnRoutes1 := []bgp.WithdrawnRoute{} + bgpMessage1 := bgp.NewBGPUpdateMessage(withdrawnRoutes1, pathAttributes1, nlri1) + + // high origin message + origin2 := bgp.NewPathAttributeOrigin(0) + aspath2 := createAsPathAttribute([]uint16{65100, 65000}) + nexthop2 := bgp.NewPathAttributeNextHop("192.168.100.1") + med2 := bgp.NewPathAttributeMultiExitDisc(100) + localpref2 := bgp.NewPathAttributeLocalPref(100) + + pathAttributes2 := []bgp.PathAttributeInterface{ + origin2, aspath2, nexthop2, med2, localpref2, + } + nlri2 := []bgp.NLRInfo{*bgp.NewNLRInfo(24, "10.10.10.0")} + withdrawnRoutes2 := []bgp.WithdrawnRoute{} + bgpMessage2 := bgp.NewBGPUpdateMessage(withdrawnRoutes2, pathAttributes2, nlri2) + + peer1 := peerR1() + pList, wList, err = tm.ProcessUpdate(peer1, bgpMessage1) + assert.Equal(t, 1, len(pList)) + assert.Equal(t, 0, len(wList)) + assert.NoError(t, err) + + peer2 := peerR2() + pList, wList, err = tm.ProcessUpdate(peer2, bgpMessage2) + assert.Equal(t, 1, len(pList)) + assert.Equal(t, 0, len(wList)) + assert.NoError(t, err) + + // check type + path := pList[0] + expectedType := "*table.IPv4Path" + assert.Equal(t, reflect.TypeOf(path).String(), expectedType) + + // check PathAttribute + pathAttributes := bgpMessage2.Body.(*bgp.BGPUpdate).PathAttributes + expectedOrigin := pathAttributes[0] + pathOrigin := path.getPathAttribute(bgp.BGP_ATTR_TYPE_ORIGIN).(*bgp.PathAttributeOrigin) + assert.Equal(t, pathOrigin, expectedOrigin) + + expectedAsPath := pathAttributes[1] + pathAspath := path.getPathAttribute(bgp.BGP_ATTR_TYPE_AS_PATH).(*bgp.PathAttributeAsPath) + assert.Equal(t, pathAspath, expectedAsPath) + + expectedNexthopAttr := pathAttributes[2] + pathNexthop := path.getPathAttribute(bgp.BGP_ATTR_TYPE_NEXT_HOP).(*bgp.PathAttributeNextHop) + assert.Equal(t, pathNexthop, expectedNexthopAttr) + + expectedMed := pathAttributes[3] + pathMed := path.getPathAttribute(bgp.BGP_ATTR_TYPE_MULTI_EXIT_DISC).(*bgp.PathAttributeMultiExitDisc) + assert.Equal(t, expectedMed, pathMed) + + // check PathAttribute length + assert.Equal(t, len(pathAttributes2), path.getPathAttributeMap().Len()) + + // check destination + expectedPrefix := "10.10.10.0" + assert.Equal(t, expectedPrefix, path.getPrefix().String()) + // check nexthop + expectedNexthop := "192.168.100.1" + assert.Equal(t, expectedNexthop, path.getNexthop().String()) + +} + +func TestProcessBGPUpdate_4_select_low_origin_ipv6(t *testing.T) { + + tm := NewTableManager() + var pList, wList []Path + var err error + + origin1 := bgp.NewPathAttributeOrigin(1) + aspath1 := createAsPathAttribute([]uint16{65200, 65000}) + mp_reach1 := createMpReach("2001::192:168:50:1", "2001:123:123:1::", 64) + med1 := bgp.NewPathAttributeMultiExitDisc(100) + localpref1 := bgp.NewPathAttributeLocalPref(100) + + pathAttributes1 := []bgp.PathAttributeInterface{ + mp_reach1, origin1, aspath1, med1, localpref1, + } + + nlri1 := []bgp.NLRInfo{} + withdrawnRoutes1 := []bgp.WithdrawnRoute{} + bgpMessage1 := bgp.NewBGPUpdateMessage(withdrawnRoutes1, pathAttributes1, nlri1) + + origin2 := bgp.NewPathAttributeOrigin(0) + aspath2 := createAsPathAttribute([]uint16{65100, 65000}) + mp_reach2 := createMpReach("2001::192:168:100:1", "2001:123:123:1::", 64) + med2 := bgp.NewPathAttributeMultiExitDisc(100) + localpref2 := bgp.NewPathAttributeLocalPref(200) + + pathAttributes2 := []bgp.PathAttributeInterface{ + mp_reach2, origin2, aspath2, med2, localpref2, + } + + nlri2 := []bgp.NLRInfo{} + withdrawnRoutes2 := []bgp.WithdrawnRoute{} + bgpMessage2 := bgp.NewBGPUpdateMessage(withdrawnRoutes2, pathAttributes2, nlri2) + + peer1 := peerR1() + pList, wList, err = tm.ProcessUpdate(peer1, bgpMessage1) + assert.Equal(t, 1, len(pList)) + assert.Equal(t, 0, len(wList)) + assert.NoError(t, err, "err should be nil") + + peer2 := peerR2() + pList, wList, err = tm.ProcessUpdate(peer2, bgpMessage2) + assert.Equal(t, 1, len(pList)) + assert.Equal(t, 0, len(wList)) + assert.NoError(t, err, "err should be nil") + + // check type + path := pList[0] + expectedType := "*table.IPv6Path" + assert.Equal(t, reflect.TypeOf(path).String(), expectedType) + + // check PathAttribute + pathAttributes := bgpMessage2.Body.(*bgp.BGPUpdate).PathAttributes + + expectedNexthopAttr := pathAttributes[0] + pathNexthop := path.getPathAttribute(bgp.BGP_ATTR_TYPE_MP_REACH_NLRI).(*bgp.PathAttributeMpReachNLRI) + assert.Equal(t, pathNexthop, expectedNexthopAttr) + + expectedOrigin := pathAttributes[1] + pathOrigin := path.getPathAttribute(bgp.BGP_ATTR_TYPE_ORIGIN).(*bgp.PathAttributeOrigin) + assert.Equal(t, pathOrigin, expectedOrigin) + + expectedAsPath := pathAttributes[2] + pathAspath := path.getPathAttribute(bgp.BGP_ATTR_TYPE_AS_PATH).(*bgp.PathAttributeAsPath) + assert.Equal(t, pathAspath, expectedAsPath) + + expectedMed := pathAttributes[3] + pathMed := path.getPathAttribute(bgp.BGP_ATTR_TYPE_MULTI_EXIT_DISC).(*bgp.PathAttributeMultiExitDisc) + assert.Equal(t, expectedMed, pathMed) + + // check PathAttribute length + assert.Equal(t, 5, path.getPathAttributeMap().Len()) + + // check destination + expectedPrefix := "2001:123:123:1::" + assert.Equal(t, expectedPrefix, path.getPrefix().String()) + // check nexthop + expectedNexthop := "2001::192:168:100:1" + assert.Equal(t, expectedNexthop, path.getNexthop().String()) + +} + +// test: compare MED +func TestProcessBGPUpdate_5_select_low_med_ipv4(t *testing.T) { + + tm := NewTableManager() + var pList, wList []Path + var err error + + // low origin message + origin1 := bgp.NewPathAttributeOrigin(0) + aspath1 := createAsPathAttribute([]uint16{65200, 65000}) + nexthop1 := bgp.NewPathAttributeNextHop("192.168.50.1") + med1 := bgp.NewPathAttributeMultiExitDisc(500) + localpref1 := bgp.NewPathAttributeLocalPref(100) + + pathAttributes1 := []bgp.PathAttributeInterface{ + origin1, aspath1, nexthop1, med1, localpref1, + } + nlri1 := []bgp.NLRInfo{*bgp.NewNLRInfo(24, "10.10.10.0")} + withdrawnRoutes1 := []bgp.WithdrawnRoute{} + bgpMessage1 := bgp.NewBGPUpdateMessage(withdrawnRoutes1, pathAttributes1, nlri1) + + // high origin message + origin2 := bgp.NewPathAttributeOrigin(0) + aspath2 := createAsPathAttribute([]uint16{65100, 65000}) + nexthop2 := bgp.NewPathAttributeNextHop("192.168.100.1") + med2 := bgp.NewPathAttributeMultiExitDisc(100) + localpref2 := bgp.NewPathAttributeLocalPref(100) + + pathAttributes2 := []bgp.PathAttributeInterface{ + origin2, aspath2, nexthop2, med2, localpref2, + } + nlri2 := []bgp.NLRInfo{*bgp.NewNLRInfo(24, "10.10.10.0")} + withdrawnRoutes2 := []bgp.WithdrawnRoute{} + bgpMessage2 := bgp.NewBGPUpdateMessage(withdrawnRoutes2, pathAttributes2, nlri2) + + peer1 := peerR1() + pList, wList, err = tm.ProcessUpdate(peer1, bgpMessage1) + assert.Equal(t, 1, len(pList)) + assert.Equal(t, 0, len(wList)) + assert.NoError(t, err) + + peer2 := peerR2() + pList, wList, err = tm.ProcessUpdate(peer2, bgpMessage2) + assert.Equal(t, 1, len(pList)) + assert.Equal(t, 0, len(wList)) + assert.NoError(t, err) + + // check type + path := pList[0] + expectedType := "*table.IPv4Path" + assert.Equal(t, reflect.TypeOf(path).String(), expectedType) + + // check PathAttribute + pathAttributes := bgpMessage2.Body.(*bgp.BGPUpdate).PathAttributes + expectedOrigin := pathAttributes[0] + pathOrigin := path.getPathAttribute(bgp.BGP_ATTR_TYPE_ORIGIN).(*bgp.PathAttributeOrigin) + assert.Equal(t, pathOrigin, expectedOrigin) + + expectedAsPath := pathAttributes[1] + pathAspath := path.getPathAttribute(bgp.BGP_ATTR_TYPE_AS_PATH).(*bgp.PathAttributeAsPath) + assert.Equal(t, pathAspath, expectedAsPath) + + expectedNexthopAttr := pathAttributes[2] + pathNexthop := path.getPathAttribute(bgp.BGP_ATTR_TYPE_NEXT_HOP).(*bgp.PathAttributeNextHop) + assert.Equal(t, pathNexthop, expectedNexthopAttr) + + expectedMed := pathAttributes[3] + pathMed := path.getPathAttribute(bgp.BGP_ATTR_TYPE_MULTI_EXIT_DISC).(*bgp.PathAttributeMultiExitDisc) + assert.Equal(t, expectedMed, pathMed) + + // check PathAttribute length + assert.Equal(t, len(pathAttributes2), path.getPathAttributeMap().Len()) + + // check destination + expectedPrefix := "10.10.10.0" + assert.Equal(t, expectedPrefix, path.getPrefix().String()) + // check nexthop + expectedNexthop := "192.168.100.1" + assert.Equal(t, expectedNexthop, path.getNexthop().String()) + +} + +func TestProcessBGPUpdate_5_select_low_med_ipv6(t *testing.T) { + + tm := NewTableManager() + var pList, wList []Path + var err error + + origin1 := bgp.NewPathAttributeOrigin(0) + aspath1 := createAsPathAttribute([]uint16{65200, 65000}) + mp_reach1 := createMpReach("2001::192:168:50:1", "2001:123:123:1::", 64) + med1 := bgp.NewPathAttributeMultiExitDisc(500) + localpref1 := bgp.NewPathAttributeLocalPref(100) + + pathAttributes1 := []bgp.PathAttributeInterface{ + mp_reach1, origin1, aspath1, med1, localpref1, + } + + nlri1 := []bgp.NLRInfo{} + withdrawnRoutes1 := []bgp.WithdrawnRoute{} + bgpMessage1 := bgp.NewBGPUpdateMessage(withdrawnRoutes1, pathAttributes1, nlri1) + + origin2 := bgp.NewPathAttributeOrigin(0) + aspath2 := createAsPathAttribute([]uint16{65100, 65000}) + mp_reach2 := createMpReach("2001::192:168:100:1", "2001:123:123:1::", 64) + med2 := bgp.NewPathAttributeMultiExitDisc(200) + localpref2 := bgp.NewPathAttributeLocalPref(100) + + pathAttributes2 := []bgp.PathAttributeInterface{ + mp_reach2, origin2, aspath2, med2, localpref2, + } + + nlri2 := []bgp.NLRInfo{} + withdrawnRoutes2 := []bgp.WithdrawnRoute{} + bgpMessage2 := bgp.NewBGPUpdateMessage(withdrawnRoutes2, pathAttributes2, nlri2) + + peer1 := peerR1() + pList, wList, err = tm.ProcessUpdate(peer1, bgpMessage1) + assert.Equal(t, 1, len(pList)) + assert.Equal(t, 0, len(wList)) + assert.NoError(t, err, "err should be nil") + + peer2 := peerR2() + pList, wList, err = tm.ProcessUpdate(peer2, bgpMessage2) + assert.Equal(t, 1, len(pList)) + assert.Equal(t, 0, len(wList)) + assert.NoError(t, err, "err should be nil") + + // check type + path := pList[0] + expectedType := "*table.IPv6Path" + assert.Equal(t, reflect.TypeOf(path).String(), expectedType) + + // check PathAttribute + pathAttributes := bgpMessage2.Body.(*bgp.BGPUpdate).PathAttributes + + expectedNexthopAttr := pathAttributes[0] + pathNexthop := path.getPathAttribute(bgp.BGP_ATTR_TYPE_MP_REACH_NLRI).(*bgp.PathAttributeMpReachNLRI) + assert.Equal(t, pathNexthop, expectedNexthopAttr) + + expectedOrigin := pathAttributes[1] + pathOrigin := path.getPathAttribute(bgp.BGP_ATTR_TYPE_ORIGIN).(*bgp.PathAttributeOrigin) + assert.Equal(t, pathOrigin, expectedOrigin) + + expectedAsPath := pathAttributes[2] + pathAspath := path.getPathAttribute(bgp.BGP_ATTR_TYPE_AS_PATH).(*bgp.PathAttributeAsPath) + assert.Equal(t, pathAspath, expectedAsPath) + + expectedMed := pathAttributes[3] + pathMed := path.getPathAttribute(bgp.BGP_ATTR_TYPE_MULTI_EXIT_DISC).(*bgp.PathAttributeMultiExitDisc) + assert.Equal(t, expectedMed, pathMed) + + // check PathAttribute length + assert.Equal(t, 5, path.getPathAttributeMap().Len()) + + // check destination + expectedPrefix := "2001:123:123:1::" + assert.Equal(t, expectedPrefix, path.getPrefix().String()) + // check nexthop + expectedNexthop := "2001::192:168:100:1" + assert.Equal(t, expectedNexthop, path.getNexthop().String()) + +} + +// test: compare AS_NUMBER(prefer eBGP path) +func TestProcessBGPUpdate_6_select_ebgp_path_ipv4(t *testing.T) { + + tm := NewTableManager() + tm.localAsn = uint32(65000) + + var pList, wList []Path + var err error + + // low origin message + origin1 := bgp.NewPathAttributeOrigin(0) + aspath1 := createAsPathAttribute([]uint16{65000, 65200}) + nexthop1 := bgp.NewPathAttributeNextHop("192.168.50.1") + med1 := bgp.NewPathAttributeMultiExitDisc(200) + localpref1 := bgp.NewPathAttributeLocalPref(100) + + pathAttributes1 := []bgp.PathAttributeInterface{ + origin1, aspath1, nexthop1, med1, localpref1, + } + nlri1 := []bgp.NLRInfo{*bgp.NewNLRInfo(24, "10.10.10.0")} + withdrawnRoutes1 := []bgp.WithdrawnRoute{} + bgpMessage1 := bgp.NewBGPUpdateMessage(withdrawnRoutes1, pathAttributes1, nlri1) + + // high origin message + origin2 := bgp.NewPathAttributeOrigin(0) + aspath2 := createAsPathAttribute([]uint16{65100, 65000}) + nexthop2 := bgp.NewPathAttributeNextHop("192.168.100.1") + med2 := bgp.NewPathAttributeMultiExitDisc(200) + localpref2 := bgp.NewPathAttributeLocalPref(100) + + pathAttributes2 := []bgp.PathAttributeInterface{ + origin2, aspath2, nexthop2, med2, localpref2, + } + nlri2 := []bgp.NLRInfo{*bgp.NewNLRInfo(24, "10.10.10.0")} + withdrawnRoutes2 := []bgp.WithdrawnRoute{} + bgpMessage2 := bgp.NewBGPUpdateMessage(withdrawnRoutes2, pathAttributes2, nlri2) + + peer1 := peerR1() + pList, wList, err = tm.ProcessUpdate(peer1, bgpMessage1) + assert.Equal(t, 1, len(pList)) + assert.Equal(t, 0, len(wList)) + assert.NoError(t, err) + + peer2 := peerR2() + pList, wList, err = tm.ProcessUpdate(peer2, bgpMessage2) + assert.Equal(t, 1, len(pList)) + assert.Equal(t, 0, len(wList)) + assert.NoError(t, err) + + // check type + path := pList[0] + expectedType := "*table.IPv4Path" + assert.Equal(t, reflect.TypeOf(path).String(), expectedType) + + // check PathAttribute + pathAttributes := bgpMessage2.Body.(*bgp.BGPUpdate).PathAttributes + expectedOrigin := pathAttributes[0] + pathOrigin := path.getPathAttribute(bgp.BGP_ATTR_TYPE_ORIGIN).(*bgp.PathAttributeOrigin) + assert.Equal(t, pathOrigin, expectedOrigin) + + expectedAsPath := pathAttributes[1] + pathAspath := path.getPathAttribute(bgp.BGP_ATTR_TYPE_AS_PATH).(*bgp.PathAttributeAsPath) + assert.Equal(t, pathAspath, expectedAsPath) + + expectedNexthopAttr := pathAttributes[2] + pathNexthop := path.getPathAttribute(bgp.BGP_ATTR_TYPE_NEXT_HOP).(*bgp.PathAttributeNextHop) + assert.Equal(t, pathNexthop, expectedNexthopAttr) + + expectedMed := pathAttributes[3] + pathMed := path.getPathAttribute(bgp.BGP_ATTR_TYPE_MULTI_EXIT_DISC).(*bgp.PathAttributeMultiExitDisc) + assert.Equal(t, expectedMed, pathMed) + + // check PathAttribute length + assert.Equal(t, len(pathAttributes2), path.getPathAttributeMap().Len()) + + // check destination + expectedPrefix := "10.10.10.0" + assert.Equal(t, expectedPrefix, path.getPrefix().String()) + // check nexthop + expectedNexthop := "192.168.100.1" + assert.Equal(t, expectedNexthop, path.getNexthop().String()) + +} + +func TestProcessBGPUpdate_6_select_ebgp_path_ipv6(t *testing.T) { + + tm := NewTableManager() + tm.localAsn = uint32(65000) + var pList, wList []Path + var err error + + origin1 := bgp.NewPathAttributeOrigin(0) + aspath1 := createAsPathAttribute([]uint16{65000, 65200}) + mp_reach1 := createMpReach("2001::192:168:50:1", "2001:123:123:1::", 64) + med1 := bgp.NewPathAttributeMultiExitDisc(200) + localpref1 := bgp.NewPathAttributeLocalPref(100) + + pathAttributes1 := []bgp.PathAttributeInterface{ + mp_reach1, origin1, aspath1, med1, localpref1, + } + + nlri1 := []bgp.NLRInfo{} + withdrawnRoutes1 := []bgp.WithdrawnRoute{} + bgpMessage1 := bgp.NewBGPUpdateMessage(withdrawnRoutes1, pathAttributes1, nlri1) + + origin2 := bgp.NewPathAttributeOrigin(0) + aspath2 := createAsPathAttribute([]uint16{65100, 65200}) + mp_reach2 := createMpReach("2001::192:168:100:1", "2001:123:123:1::", 64) + med2 := bgp.NewPathAttributeMultiExitDisc(200) + localpref2 := bgp.NewPathAttributeLocalPref(100) + + pathAttributes2 := []bgp.PathAttributeInterface{ + mp_reach2, origin2, aspath2, med2, localpref2, + } + + nlri2 := []bgp.NLRInfo{} + withdrawnRoutes2 := []bgp.WithdrawnRoute{} + bgpMessage2 := bgp.NewBGPUpdateMessage(withdrawnRoutes2, pathAttributes2, nlri2) + + peer1 := peerR1() + pList, wList, err = tm.ProcessUpdate(peer1, bgpMessage1) + assert.Equal(t, 1, len(pList)) + assert.Equal(t, 0, len(wList)) + assert.NoError(t, err, "err should be nil") + + peer2 := peerR2() + pList, wList, err = tm.ProcessUpdate(peer2, bgpMessage2) + assert.Equal(t, 1, len(pList)) + assert.Equal(t, 0, len(wList)) + assert.NoError(t, err, "err should be nil") + + // check type + path := pList[0] + expectedType := "*table.IPv6Path" + assert.Equal(t, reflect.TypeOf(path).String(), expectedType) + + // check PathAttribute + pathAttributes := bgpMessage2.Body.(*bgp.BGPUpdate).PathAttributes + + expectedNexthopAttr := pathAttributes[0] + pathNexthop := path.getPathAttribute(bgp.BGP_ATTR_TYPE_MP_REACH_NLRI).(*bgp.PathAttributeMpReachNLRI) + assert.Equal(t, pathNexthop, expectedNexthopAttr) + + expectedOrigin := pathAttributes[1] + pathOrigin := path.getPathAttribute(bgp.BGP_ATTR_TYPE_ORIGIN).(*bgp.PathAttributeOrigin) + assert.Equal(t, pathOrigin, expectedOrigin) + + expectedAsPath := pathAttributes[2] + pathAspath := path.getPathAttribute(bgp.BGP_ATTR_TYPE_AS_PATH).(*bgp.PathAttributeAsPath) + assert.Equal(t, pathAspath, expectedAsPath) + + expectedMed := pathAttributes[3] + pathMed := path.getPathAttribute(bgp.BGP_ATTR_TYPE_MULTI_EXIT_DISC).(*bgp.PathAttributeMultiExitDisc) + assert.Equal(t, expectedMed, pathMed) + + // check PathAttribute length + assert.Equal(t, 5, path.getPathAttributeMap().Len()) + + // check destination + expectedPrefix := "2001:123:123:1::" + assert.Equal(t, expectedPrefix, path.getPrefix().String()) + // check nexthop + expectedNexthop := "2001::192:168:100:1" + assert.Equal(t, expectedNexthop, path.getNexthop().String()) + +} + +// test: compare IGP cost -> N/A + +// test: compare Router ID +func TestProcessBGPUpdate_7_select_low_routerid_path_ipv4(t *testing.T) { + + tm := NewTableManager() + tm.localAsn = uint32(65000) + + var pList, wList []Path + var err error + + // low origin message + origin1 := bgp.NewPathAttributeOrigin(0) + aspath1 := createAsPathAttribute([]uint16{65000, 65200}) + nexthop1 := bgp.NewPathAttributeNextHop("192.168.50.1") + med1 := bgp.NewPathAttributeMultiExitDisc(200) + localpref1 := bgp.NewPathAttributeLocalPref(100) + + pathAttributes1 := []bgp.PathAttributeInterface{ + origin1, aspath1, nexthop1, med1, localpref1, + } + nlri1 := []bgp.NLRInfo{*bgp.NewNLRInfo(24, "10.10.10.0")} + withdrawnRoutes1 := []bgp.WithdrawnRoute{} + bgpMessage1 := bgp.NewBGPUpdateMessage(withdrawnRoutes1, pathAttributes1, nlri1) + + // high origin message + origin2 := bgp.NewPathAttributeOrigin(0) + aspath2 := createAsPathAttribute([]uint16{65000, 65100}) + nexthop2 := bgp.NewPathAttributeNextHop("192.168.100.1") + med2 := bgp.NewPathAttributeMultiExitDisc(200) + localpref2 := bgp.NewPathAttributeLocalPref(100) + + pathAttributes2 := []bgp.PathAttributeInterface{ + origin2, aspath2, nexthop2, med2, localpref2, + } + nlri2 := []bgp.NLRInfo{*bgp.NewNLRInfo(24, "10.10.10.0")} + withdrawnRoutes2 := []bgp.WithdrawnRoute{} + bgpMessage2 := bgp.NewBGPUpdateMessage(withdrawnRoutes2, pathAttributes2, nlri2) + + peer1 := peerR1() + pList, wList, err = tm.ProcessUpdate(peer1, bgpMessage1) + assert.Equal(t, 1, len(pList)) + assert.Equal(t, 0, len(wList)) + assert.NoError(t, err) + + peer3 := peerR3() + pList, wList, err = tm.ProcessUpdate(peer3, bgpMessage2) + assert.Equal(t, 1, len(pList)) + assert.Equal(t, 0, len(wList)) + assert.NoError(t, err) + + // check type + path := pList[0] + expectedType := "*table.IPv4Path" + assert.Equal(t, reflect.TypeOf(path).String(), expectedType) + + // check PathAttribute + pathAttributes := bgpMessage2.Body.(*bgp.BGPUpdate).PathAttributes + expectedOrigin := pathAttributes[0] + pathOrigin := path.getPathAttribute(bgp.BGP_ATTR_TYPE_ORIGIN).(*bgp.PathAttributeOrigin) + assert.Equal(t, pathOrigin, expectedOrigin) + + expectedAsPath := pathAttributes[1] + pathAspath := path.getPathAttribute(bgp.BGP_ATTR_TYPE_AS_PATH).(*bgp.PathAttributeAsPath) + assert.Equal(t, pathAspath, expectedAsPath) + + expectedNexthopAttr := pathAttributes[2] + pathNexthop := path.getPathAttribute(bgp.BGP_ATTR_TYPE_NEXT_HOP).(*bgp.PathAttributeNextHop) + assert.Equal(t, pathNexthop, expectedNexthopAttr) + + expectedMed := pathAttributes[3] + pathMed := path.getPathAttribute(bgp.BGP_ATTR_TYPE_MULTI_EXIT_DISC).(*bgp.PathAttributeMultiExitDisc) + assert.Equal(t, expectedMed, pathMed) + + // check PathAttribute length + assert.Equal(t, len(pathAttributes2), path.getPathAttributeMap().Len()) + + // check destination + expectedPrefix := "10.10.10.0" + assert.Equal(t, expectedPrefix, path.getPrefix().String()) + // check nexthop + expectedNexthop := "192.168.100.1" + assert.Equal(t, expectedNexthop, path.getNexthop().String()) + +} + +func TestProcessBGPUpdate_7_select_low_routerid_path_ipv6(t *testing.T) { + + tm := NewTableManager() + tm.localAsn = uint32(65000) + var pList, wList []Path + var err error + + origin1 := bgp.NewPathAttributeOrigin(0) + aspath1 := createAsPathAttribute([]uint16{65000, 65200}) + mp_reach1 := createMpReach("2001::192:168:50:1", "2001:123:123:1::", 64) + med1 := bgp.NewPathAttributeMultiExitDisc(200) + localpref1 := bgp.NewPathAttributeLocalPref(100) + + pathAttributes1 := []bgp.PathAttributeInterface{ + mp_reach1, origin1, aspath1, med1, localpref1, + } + + nlri1 := []bgp.NLRInfo{} + withdrawnRoutes1 := []bgp.WithdrawnRoute{} + bgpMessage1 := bgp.NewBGPUpdateMessage(withdrawnRoutes1, pathAttributes1, nlri1) + + origin2 := bgp.NewPathAttributeOrigin(0) + aspath2 := createAsPathAttribute([]uint16{65100, 65200}) + mp_reach2 := createMpReach("2001::192:168:100:1", "2001:123:123:1::", 64) + med2 := bgp.NewPathAttributeMultiExitDisc(200) + localpref2 := bgp.NewPathAttributeLocalPref(100) + + pathAttributes2 := []bgp.PathAttributeInterface{ + mp_reach2, origin2, aspath2, med2, localpref2, + } + + nlri2 := []bgp.NLRInfo{} + withdrawnRoutes2 := []bgp.WithdrawnRoute{} + bgpMessage2 := bgp.NewBGPUpdateMessage(withdrawnRoutes2, pathAttributes2, nlri2) + + peer1 := peerR1() + pList, wList, err = tm.ProcessUpdate(peer1, bgpMessage1) + assert.Equal(t, 1, len(pList)) + assert.Equal(t, 0, len(wList)) + assert.NoError(t, err, "err should be nil") + + peer3 := peerR3() + pList, wList, err = tm.ProcessUpdate(peer3, bgpMessage2) + assert.Equal(t, 1, len(pList)) + assert.Equal(t, 0, len(wList)) + assert.NoError(t, err, "err should be nil") + + // check type + path := pList[0] + expectedType := "*table.IPv6Path" + assert.Equal(t, reflect.TypeOf(path).String(), expectedType) + + // check PathAttribute + pathAttributes := bgpMessage2.Body.(*bgp.BGPUpdate).PathAttributes + + expectedNexthopAttr := pathAttributes[0] + pathNexthop := path.getPathAttribute(bgp.BGP_ATTR_TYPE_MP_REACH_NLRI).(*bgp.PathAttributeMpReachNLRI) + assert.Equal(t, pathNexthop, expectedNexthopAttr) + + expectedOrigin := pathAttributes[1] + pathOrigin := path.getPathAttribute(bgp.BGP_ATTR_TYPE_ORIGIN).(*bgp.PathAttributeOrigin) + assert.Equal(t, pathOrigin, expectedOrigin) + + expectedAsPath := pathAttributes[2] + pathAspath := path.getPathAttribute(bgp.BGP_ATTR_TYPE_AS_PATH).(*bgp.PathAttributeAsPath) + assert.Equal(t, pathAspath, expectedAsPath) + + expectedMed := pathAttributes[3] + pathMed := path.getPathAttribute(bgp.BGP_ATTR_TYPE_MULTI_EXIT_DISC).(*bgp.PathAttributeMultiExitDisc) + assert.Equal(t, expectedMed, pathMed) + + // check PathAttribute length + assert.Equal(t, 5, path.getPathAttributeMap().Len()) + + // check destination + expectedPrefix := "2001:123:123:1::" + assert.Equal(t, expectedPrefix, path.getPrefix().String()) + // check nexthop + expectedNexthop := "2001::192:168:100:1" + assert.Equal(t, expectedNexthop, path.getNexthop().String()) + +} + +func update_fromR1() *bgp.BGPMessage { + + origin := bgp.NewPathAttributeOrigin(0) + aspathParam := []bgp.AsPathParamInterface{bgp.NewAsPathParam(2, []uint16{65000})} + aspath := bgp.NewPathAttributeAsPath(aspathParam) + nexthop := bgp.NewPathAttributeNextHop("192.168.50.1") + med := bgp.NewPathAttributeMultiExitDisc(0) + + pathAttributes := []bgp.PathAttributeInterface{ + origin, + aspath, + nexthop, + med, + } + + nlri := []bgp.NLRInfo{*bgp.NewNLRInfo(24, "10.10.10.0")} + withdrawnRoutes := []bgp.WithdrawnRoute{} + + return bgp.NewBGPUpdateMessage(withdrawnRoutes, pathAttributes, nlri) +} + +func update_fromR1_ipv6() *bgp.BGPMessage { + + origin := bgp.NewPathAttributeOrigin(0) + aspathParam := []bgp.AsPathParamInterface{bgp.NewAsPathParam(2, []uint16{65000})} + aspath := bgp.NewPathAttributeAsPath(aspathParam) + + mp_nlri := []bgp.AddrPrefixInterface{bgp.NewIPv6AddrPrefix(64, "2001:123:123:1::")} + mp_reach := bgp.NewPathAttributeMpReachNLRI("2001::192:168:50:1", mp_nlri) + med := bgp.NewPathAttributeMultiExitDisc(0) + + pathAttributes := []bgp.PathAttributeInterface{ + mp_reach, + origin, + aspath, + med, + } + nlri := []bgp.NLRInfo{} + withdrawnRoutes := []bgp.WithdrawnRoute{} + return bgp.NewBGPUpdateMessage(withdrawnRoutes, pathAttributes, nlri) +} + +func update_fromR2() *bgp.BGPMessage { + + origin := bgp.NewPathAttributeOrigin(0) + aspathParam := []bgp.AsPathParamInterface{bgp.NewAsPathParam(2, []uint16{65100})} + aspath := bgp.NewPathAttributeAsPath(aspathParam) + nexthop := bgp.NewPathAttributeNextHop("192.168.100.1") + med := bgp.NewPathAttributeMultiExitDisc(100) + + pathAttributes := []bgp.PathAttributeInterface{ + origin, + aspath, + nexthop, + med, + } + + nlri := []bgp.NLRInfo{*bgp.NewNLRInfo(24, "20.20.20.0")} + withdrawnRoutes := []bgp.WithdrawnRoute{} + return bgp.NewBGPUpdateMessage(withdrawnRoutes, pathAttributes, nlri) +} + +func update_fromR2_ipv6() *bgp.BGPMessage { + + origin := bgp.NewPathAttributeOrigin(0) + aspath := createAsPathAttribute([]uint16{65100}) + mp_reach := createMpReach("2001::192:168:100:1", "2002:223:123:1::", 64) + med := bgp.NewPathAttributeMultiExitDisc(100) + + pathAttributes := []bgp.PathAttributeInterface{ + mp_reach, + origin, + aspath, + med, + } + nlri := []bgp.NLRInfo{} + withdrawnRoutes := []bgp.WithdrawnRoute{} + return bgp.NewBGPUpdateMessage(withdrawnRoutes, pathAttributes, nlri) +} + +func createAsPathAttribute(ases []uint16) *bgp.PathAttributeAsPath { + aspathParam := []bgp.AsPathParamInterface{bgp.NewAsPathParam(2, ases)} + aspath := bgp.NewPathAttributeAsPath(aspathParam) + return aspath +} + +func createMpReach(nexthop, nlri string, len uint8) *bgp.PathAttributeMpReachNLRI { + mp_nlri := []bgp.AddrPrefixInterface{bgp.NewIPv6AddrPrefix(len, nlri)} + mp_reach := bgp.NewPathAttributeMpReachNLRI(nexthop, mp_nlri) + return mp_reach +} + +func update_fromR2viaR1() *bgp.BGPMessage { + + origin := bgp.NewPathAttributeOrigin(0) + aspathParam := []bgp.AsPathParamInterface{bgp.NewAsPathParam(2, []uint16{65000, 65100})} + aspath := bgp.NewPathAttributeAsPath(aspathParam) + nexthop := bgp.NewPathAttributeNextHop("192.168.50.1") + + pathAttributes := []bgp.PathAttributeInterface{ + origin, + aspath, + nexthop, + } + + nlri := []bgp.NLRInfo{*bgp.NewNLRInfo(24, "20.20.20.0")} + withdrawnRoutes := []bgp.WithdrawnRoute{} + return bgp.NewBGPUpdateMessage(withdrawnRoutes, pathAttributes, nlri) +} + +func update_fromR2viaR1_ipv6() *bgp.BGPMessage { + + origin := bgp.NewPathAttributeOrigin(0) + aspath := createAsPathAttribute([]uint16{65000, 65100}) + mp_reach := createMpReach("2001::192:168:50:1", "2002:223:123:1::", 64) + med := bgp.NewPathAttributeMultiExitDisc(100) + + pathAttributes := []bgp.PathAttributeInterface{ + mp_reach, + origin, + aspath, + med, + } + nlri := []bgp.NLRInfo{} + withdrawnRoutes := []bgp.WithdrawnRoute{} + return bgp.NewBGPUpdateMessage(withdrawnRoutes, pathAttributes, nlri) + +} + +func update() *bgp.BGPMessage { + w1 := bgp.WithdrawnRoute{*bgp.NewIPAddrPrefix(23, "121.1.3.2")} + w2 := bgp.WithdrawnRoute{*bgp.NewIPAddrPrefix(17, "100.33.3.0")} + w := []bgp.WithdrawnRoute{w1, w2} + //w := []WithdrawnRoute{} + + aspath1 := []bgp.AsPathParamInterface{ + bgp.NewAsPathParam(2, []uint16{1000}), + bgp.NewAsPathParam(1, []uint16{1001, 1002}), + bgp.NewAsPathParam(2, []uint16{1003, 1004}), + } + + aspath2 := []bgp.AsPathParamInterface{ + bgp.NewAs4PathParam(2, []uint32{1000000}), + bgp.NewAs4PathParam(1, []uint32{1000001, 1002}), + bgp.NewAs4PathParam(2, []uint32{1003, 100004}), + } + + aspath3 := []*bgp.As4PathParam{ + bgp.NewAs4PathParam(2, []uint32{1000000}), + bgp.NewAs4PathParam(1, []uint32{1000001, 1002}), + bgp.NewAs4PathParam(2, []uint32{1003, 100004}), + } + + ecommunities := []bgp.ExtendedCommunityInterface{ + &bgp.TwoOctetAsSpecificExtended{SubType: 1, AS: 10003, LocalAdmin: 3 << 20}, + &bgp.FourOctetAsSpecificExtended{SubType: 2, AS: 1 << 20, LocalAdmin: 300}, + &bgp.IPv4AddressSpecificExtended{SubType: 3, IPv4: net.ParseIP("192.2.1.2").To4(), LocalAdmin: 3000}, + &bgp.OpaqueExtended{Value: []byte{0, 1, 2, 3, 4, 5, 6, 7}}, + &bgp.UnknownExtended{Type: 99, Value: []byte{0, 1, 2, 3, 4, 5, 6, 7}}, + } + + mp_nlri := []bgp.AddrPrefixInterface{ + bgp.NewLabelledVPNIPAddrPrefix(20, "192.0.9.0", *bgp.NewLabel(1, 2, 3), + bgp.NewRouteDistinguisherTwoOctetAS(256, 10000)), + bgp.NewLabelledVPNIPAddrPrefix(26, "192.10.8.192", *bgp.NewLabel(5, 6, 7, 8), + bgp.NewRouteDistinguisherIPAddressAS("10.0.1.1", 10001)), + } + + mp_nlri2 := []bgp.AddrPrefixInterface{bgp.NewIPv6AddrPrefix(100, + "fe80:1234:1234:5667:8967:af12:8912:1023")} + + mp_nlri3 := []bgp.AddrPrefixInterface{bgp.NewLabelledVPNIPv6AddrPrefix(100, + "fe80:1234:1234:5667:8967:af12:1203:33a1", *bgp.NewLabel(5, 6), + bgp.NewRouteDistinguisherFourOctetAS(5, 6))} + + mp_nlri4 := []bgp.AddrPrefixInterface{bgp.NewLabelledIPAddrPrefix(25, "192.168.0.0", + *bgp.NewLabel(5, 6, 7))} + + p := []bgp.PathAttributeInterface{ + bgp.NewPathAttributeOrigin(3), + bgp.NewPathAttributeAsPath(aspath1), + bgp.NewPathAttributeAsPath(aspath2), + bgp.NewPathAttributeNextHop("129.1.1.2"), + bgp.NewPathAttributeMultiExitDisc(1 << 20), + bgp.NewPathAttributeLocalPref(1 << 22), + bgp.NewPathAttributeAtomicAggregate(), + bgp.NewPathAttributeAggregator(uint16(30002), "129.0.2.99"), + bgp.NewPathAttributeAggregator(uint32(30002), "129.0.2.99"), + bgp.NewPathAttributeAggregator(uint32(300020), "129.0.2.99"), + bgp.NewPathAttributeCommunities([]uint32{1, 3}), + bgp.NewPathAttributeOriginatorId("10.10.0.1"), + bgp.NewPathAttributeClusterList([]string{"10.10.0.2", "10.10.0.3"}), + bgp.NewPathAttributeExtendedCommunities(ecommunities), + bgp.NewPathAttributeAs4Path(aspath3), + bgp.NewPathAttributeAs4Aggregator(10000, "112.22.2.1"), + bgp.NewPathAttributeMpReachNLRI("112.22.2.0", mp_nlri), + bgp.NewPathAttributeMpReachNLRI("1023::", mp_nlri2), + bgp.NewPathAttributeMpReachNLRI("fe80::", mp_nlri3), + bgp.NewPathAttributeMpReachNLRI("129.1.1.1", mp_nlri4), + bgp.NewPathAttributeMpUnreachNLRI(mp_nlri), + &bgp.PathAttributeUnknown{ + PathAttribute: bgp.PathAttribute{ + Flags: 1, + Type: 100, + Value: []byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}, + }, + }, + } + n := []bgp.NLRInfo{*bgp.NewNLRInfo(24, "13.2.3.1")} + return bgp.NewBGPUpdateMessage(w, p, n) +} diff --git a/table/temporary_structs.go b/table/temporary_structs.go new file mode 100644 index 00000000..6350d2e7 --- /dev/null +++ b/table/temporary_structs.go @@ -0,0 +1,34 @@ +// core.go +package table + +import ( + "github.com/osrg/gobgp/packet" + "net" +) + +type CoreService struct { + CommonConf *Commons + NeighborsConf *Neighbors +} +type Neighbors struct { + //need to define a structure +} +type Commons struct { + //need to define a structure +} +type Peer struct { + //need to define a structure + RemoteAs uint32 + VersionNum int + RemoteAddress net.IP + protocol *BgpProtocol +} +type SentRoute struct { + path Path + peer *Peer +} +type BgpProtocol struct { + //need to define a structure + recvOpenMsg *bgp.BGPOpen + sentOpenMsg *bgp.BGPOpen +} diff --git a/utils/collection.go b/utils/collection.go new file mode 100644 index 00000000..3f6e2ed1 --- /dev/null +++ b/utils/collection.go @@ -0,0 +1,199 @@ +// 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 utils + +import ( + "container/list" + "fmt" + "reflect" +) + +// Elements of abstraction the key. +type K struct { + Element interface{} +} + +/* +// Elements of abstraction the key. +type V struct { + Element interface{} +} +*/ +/* This method may be required +func (e *E) Elem() interface{} { + return e.Element +} +*/ + +// Structure for extending the map collection. +// +// keyType : Key type of Map . +// keyType : Value type of Map. +// TMap : Map of the object to be extended. +// TOrder : Insertion order list of map. +type OrderedMap struct { + keyType interface{} + valType interface{} + TMap map[K]interface{} + TOrder *list.List +} + +// Constructor of OrderedMap. +func NewOrderedMap() *OrderedMap { + om := &OrderedMap{} + om.TMap = make(map[K]interface{}) + om.TOrder = list.New() + return om +} + +// Check whether the same of type of Map and List +// +// Returns: - error contents +// and nil if no error occurred. +func (om *OrderedMap) checkType(keyInfs interface{}, valInfs interface{}) (e error) { + if om.keyType == nil && om.valType == nil { + om.keyType = keyInfs + om.valType = valInfs + return nil + } else { + if reflect.TypeOf(om.keyType) != reflect.TypeOf(keyInfs) { + return fmt.Errorf("Map Key Type mismatch [ %s ] and [ %s ].", reflect.TypeOf(om.keyType), reflect.TypeOf(keyInfs)) + } + } + return nil +} + +// Check whether the same key exists in the map +// +// Returns: - error contents +// and nil if no error occurred. +func (om *OrderedMap) checkDuplicate(keyInfs interface{}) (e error) { + for elem := om.TOrder.Front(); elem != nil; elem = elem.Next() { + if elem.Value == keyInfs { + return fmt.Errorf("Map key Duplicated [%s].", elem.Value) + } + } + return +} + +// Append Elements to Map +// +// Returns: - error contents +// and nil if no error occurred. +func (om *OrderedMap) Append(keyInfs interface{}, valInfs interface{}) (e error) { + e = om.checkType(keyInfs, valInfs) + if e != nil { + return e + } + // Append key Elements to Map + om.TMap[K{Element: keyInfs}] = valInfs + e = om.checkDuplicate(K{Element: keyInfs}) + if e != nil { + /* + for elem := om.TOrder.Front(); elem != nil; elem = elem.Next() { + if elem.Value == (K{Element: keyInfs}) { + tmp := elem.Next() + om.TOrder.Remove(elem) + elem = tmp + } + }*/ + return nil + } + // Append Elements to List + om.TOrder.PushBack(K{Element: keyInfs}) + return nil +} + +// Get Elements from receive parameter. +// +// Returns: - Value of Map +// Return the interface that value has entered the Map. +func (om *OrderedMap) Get(keyInfs interface{}) interface{} { + elem := om.TMap[K{Element: keyInfs}] + return elem +} + +// Convert Map keys to List. +// +// Returns: - List of Map Keys +func (om *OrderedMap) KeyLists() *list.List { + keys := list.New() + for key := om.TOrder.Front(); key != nil; key = key.Next() { + keyElem := key.Value.(K).Element + keys.PushBack(keyElem) + } + return keys +} + +// Convert Map values to List. +// +// Returns: - List of Map Values +func (om *OrderedMap) ValueLists() *list.List { + vals := list.New() + for key := om.TOrder.Front(); key != nil; key = key.Next() { + keyElem := key.Value.(K).Element + value := om.Get(keyElem) + vals.PushBack(value) + } + return vals +} + +// Get Map length +// +// Returns: - Length of the map Element. +func (om *OrderedMap) Len() int { + return om.TOrder.Len() +} + +// Delete Map Element +// +// Returns: - error contents +// and nil if no error occurred. +func (om *OrderedMap) Delete(keyInfs interface{}) (e error) { + // Delete key Elements from Map + delete(om.TMap, K{Element: keyInfs}) + // Delete key Elements from List + for elem := om.TOrder.Front(); elem != nil; elem = elem.Next() { + if elem.Value == (K{Element: keyInfs}) { + tmp := elem.Next() + if tmp == nil { + break + } + om.TOrder.Remove(elem) + elem = tmp + } + } + return +} + +// Get Elements from Map and delete from List +// +// Returns: - Value of Map +// Return the interface that value has entered the Map. +func (om *OrderedMap) Pop(keyInfs interface{}) interface{} { + elem := om.TMap[K{Element: keyInfs}] + key := (K{Element: keyInfs}).Element + om.Delete(key) + return elem +} + +// Clear Map and List +func (om *OrderedMap) Clear() { + om.keyType = nil + om.valType = nil + om.TMap = make(map[K]interface{}) + om.TOrder = list.New() +} diff --git a/utils/collection_test.go b/utils/collection_test.go new file mode 100644 index 00000000..692c46f1 --- /dev/null +++ b/utils/collection_test.go @@ -0,0 +1,162 @@ +// collection_test.go +package utils + +import ( + "fmt" + "testing" +) + +func insertData(t *testing.T, oMap *OrderedMap, num int) (*OrderedMap, string) { + var e error = nil + for i := 0; i < num; i++ { + arg := "test" + key := i + value := fmt.Sprintf("%s%d", arg, key) + e = oMap.Append(key, value) + if e != nil { + t.Error(e) + } + } + result := "FAIL" + if e == nil { + result = "OK" + } + return oMap, result +} + +func getData(t *testing.T, oMap *OrderedMap, iNum int, deleteNum int) string { + var result string + for i := 0; i < iNum; i++ { + if deleteNum == i { + continue + } + arg := "test" + key := i + value := fmt.Sprintf("%s%d", arg, key) + ans := oMap.Get(i).Element + result = "OK" + //fmt.Println(ans) + if ans != value { + result = "FAIL" + break + } + } + return result +} +func deleteData(t *testing.T, oMap *OrderedMap, iNum int, deleteNum int) string { + e := oMap.Delete(deleteNum) + result := "OK" + if e != nil { + result = "FAIL" + t.Error(e) + return result + } + result = getData(t, oMap, iNum, deleteNum) + return result +} +func popData(t *testing.T, oMap *OrderedMap, iNum int, popNum int) string { + arg := "test" + value := fmt.Sprintf("%s%d", arg, popNum) + getValue := oMap.Pop(popNum).Element + result := "OK" + fmt.Println(getValue) + if value != getValue { + result = "FAIL" + t.Errorf("Different result < %s > < %s >", value, getValue) + return result + } + result = getData(t, oMap, iNum, popNum) + return result +} +func checkLen(t *testing.T, oMap *OrderedMap, iNum int) string { + mLen := oMap.Len() + result := "OK" + if mLen != iNum { + result = "FAIL" + t.Errorf("Different result < %d > < %d >", mLen, iNum) + return result + } + return result +} +func getkListData(t *testing.T, oMap *OrderedMap, iNum int) string { + kList := oMap.KeyLists() + mkLen := kList.Len() + result := "OK" + if mkLen != iNum { + result = "FAIL" + t.Errorf("Different result < %d > < %d >", mkLen, iNum) + return result + } + i := 0 + for elem := kList.Front(); elem != nil; elem = elem.Next() { + if elem.Value != i { + result = "FAIL" + break + } + i++ + } + return result +} +func getvListData(t *testing.T, oMap *OrderedMap, iNum int) string { + vList := oMap.ValueLists() + mvLen := vList.Len() + result := "OK" + if mvLen != iNum { + result = "FAIL" + t.Errorf("Different result < %d > < %d >", mvLen, iNum) + return result + } + arg := "test" + i := 0 + for elem := vList.Front(); elem != nil; elem = elem.Next() { + value := fmt.Sprintf("%s%d", arg, i) + if elem.Value != value { + result = "FAIL" + break + } + i++ + } + return result +} +func Test_Collection(t *testing.T) { + // init + var result string + iNum := 10 + deleteNum := -1 + oMap := NewOrderedMap() + // test + t.Log("# INSERT") + oMap, result = insertData(t, oMap, iNum) + t.Log("# INSERT END -> [ ", result, " ]") + t.Log("") + t.Log("# GET ELEMENT") + result = getData(t, oMap, iNum, deleteNum) + t.Log("# INSERT ELEMENT END -> [ ", result, " ]") + t.Log("") + t.Log("# DELETE ELEMENT") + deleteNum = 9 + result = deleteData(t, oMap, iNum, deleteNum) + t.Log("# DELETE ELEMENT END -> [ ", result, " ]") + t.Log("") + t.Log("# POP ELEMENT") + popNum := 9 + oMap = NewOrderedMap() + oMap, result = insertData(t, oMap, iNum) + result = popData(t, oMap, iNum, popNum) + t.Log("# POP ELEMENT END -> [ ", result, " ]") + t.Log("") + t.Log("# CHECK LEN") + oMap = NewOrderedMap() + oMap, result = insertData(t, oMap, iNum) + result = checkLen(t, oMap, iNum) + t.Log("# CHECK LEN END -> [ ", result, " ]") + t.Log("") + t.Log("# GET KEY LIST") + result = getkListData(t, oMap, iNum) + t.Log("# GET KEY LIST END -> [ ", result, " ]") + t.Log("") + t.Log("# GET VALUE LIST") + result = getvListData(t, oMap, iNum) + t.Log("# GET VALUE LIST END -> [ ", result, " ]") + t.Log("") +} |