func (a *AsPathPrependAction) apply(path *table.Path) *table.Path { var asn uint32 if a.useLeftMost { asns := path.GetAsSeqList() if len(asns) == 0 { log.WithFields(log.Fields{ "Topic": "Policy", "Type": "AsPathPrepend Action", }).Error("aspath length is zero.") return path } asn = asns[0] log.WithFields(log.Fields{ "Topic": "Policy", "Type": "AsPathPrepend Action", "LastAs": asn, "Repeat": a.repeat, }).Debug("use last AS.") } else { asn = a.asn } path.PrependAsn(asn, a.repeat) return path }
// Compare path with a policy's condition in stored order in the policy. // If a condition match, then this function stops evaluation and // subsequent conditions are skipped. func (p *Policy) Apply(path *table.Path) (bool, RouteType, *table.Path) { for _, statement := range p.Statements { result := statement.evaluate(path) log.WithFields(log.Fields{ "Topic": "Policy", "Path": path, "PolicyName": p.Name, }).Debug("statement.Conditions.evaluate : ", result) var p *table.Path if result { //Routing action p = statement.routingAction.apply(path) if p != nil { // apply all modification actions cloned := path.Clone(p.IsWithdraw) for _, action := range statement.modificationActions { cloned = action.apply(cloned) } return true, ROUTE_TYPE_ACCEPT, cloned } else { return true, ROUTE_TYPE_REJECT, nil } } } return false, ROUTE_TYPE_NONE, nil }
func applyPolicy(component, owner string, d Direction, policies []*policy.Policy, original *table.Path) (bool, *table.Path) { var applied bool = true for _, pol := range policies { if result, action, newpath := pol.Apply(original); result { log.Debug("newpath: ", newpath) if action == policy.ROUTE_TYPE_REJECT { log.WithFields(log.Fields{ "Topic": component, "Key": owner, "NRLI": original.GetNlri(), "Dir": d, }).Debug("path was rejected") // return applied, nil, this means path was rejected return applied, nil } else { // return applied, new path return applied, newpath } } } log.WithFields(log.Fields{ "Topic": component, "Key": owner, "Len": len(policies), "NRLI": original, "Dir": d, }).Debug("no policy applied") return !applied, original }
// compare AS_PATH length in the message's AS_PATH attribute with // the one in condition. func (c *AsPathLengthCondition) evaluate(path *table.Path) bool { length := uint32(path.GetAsPathLen()) result := false switch c.Operator { case ATTRIBUTE_EQ: result = c.Value == length case ATTRIBUTE_GE: result = c.Value <= length case ATTRIBUTE_LE: result = c.Value >= length default: return false } if result { log.WithFields(log.Fields{ "Topic": "Policy", "Condition": "aspath length", "Reason": c.Operator, }).Debug("condition matched") } return result }
func newIPRouteMessage(path *table.Path) *zebra.Message { l := strings.SplitN(path.GetNlri().String(), "/", 2) var command zebra.API_TYPE var prefix net.IP nexthops := []net.IP{} switch path.GetRouteFamily() { case bgp.RF_IPv4_UC: if path.IsWithdraw == true { command = zebra.IPV4_ROUTE_DELETE } else { command = zebra.IPV4_ROUTE_ADD } prefix = net.ParseIP(l[0]).To4() nexthops = append(nexthops, path.GetNexthop().To4()) case bgp.RF_IPv6_UC: if path.IsWithdraw == true { command = zebra.IPV6_ROUTE_DELETE } else { command = zebra.IPV6_ROUTE_ADD } prefix = net.ParseIP(l[0]).To16() nexthops = append(nexthops, path.GetNexthop().To16()) default: return nil } flags := uint8(zebra.MESSAGE_NEXTHOP) plen, _ := strconv.Atoi(l[1]) med, err := path.GetMed() if err == nil { flags |= zebra.MESSAGE_METRIC } return &zebra.Message{ Header: zebra.Header{ Len: zebra.HEADER_SIZE, Marker: zebra.HEADER_MARKER, Version: zebra.VERSION, Command: command, }, Body: &zebra.IPRouteBody{ Type: zebra.ROUTE_BGP, SAFI: zebra.SAFI_UNICAST, Message: flags, Prefix: prefix, PrefixLength: uint8(plen), Nexthops: nexthops, Metric: med, }, } }
func ipPrefixCalculate(path *table.Path, cPrefix Prefix) bool { rf := path.GetRouteFamily() log.Debug("path routefamily : ", rf.String()) var pAddr net.IP var pMasklen uint8 if rf != cPrefix.AddressFamily { return false } switch rf { case bgp.RF_IPv4_UC: pAddr = path.GetNlri().(*bgp.NLRInfo).IPAddrPrefix.Prefix pMasklen = path.GetNlri().(*bgp.NLRInfo).IPAddrPrefix.Length case bgp.RF_IPv6_UC: pAddr = path.GetNlri().(*bgp.IPv6AddrPrefix).Prefix pMasklen = path.GetNlri().(*bgp.IPv6AddrPrefix).Length default: return false } cp := fmt.Sprintf("%s/%d", cPrefix.Address, cPrefix.Masklength) rMin, okMin := cPrefix.MasklengthRange[MASK_LENGTH_RANGE_MIN] rMax, okMax := cPrefix.MasklengthRange[MASK_LENGTH_RANGE_MAX] if !okMin && !okMax { if pAddr.Equal(cPrefix.Address) && pMasklen == cPrefix.Masklength { return true } else { return false } } _, ipNet, e := net.ParseCIDR(cp) if e != nil { log.WithFields(log.Fields{ "Topic": "Policy", "Prefix": ipNet, "Error": e, }).Error("failed to parse the prefix of condition") return false } if ipNet.Contains(pAddr) && (rMin <= pMasklen && pMasklen <= rMax) { return true } return false }
// compare AS_PATH in the message's AS_PATH attribute with // the one in condition. func (c *AsPathCondition) evaluate(path *table.Path) bool { aspath := path.GetAsSeqList() if len(aspath) == 0 { return false } matched := false for _, member := range c.AsPathList { switch member.postiion { case AS_FROM: matched = aspath[0] == member.asn case AS_ANY: for _, n := range aspath { if n == member.asn { matched = true break } } case AS_ORIGIN: matched = aspath[len(aspath)-1] == member.asn case AS_ONLY: matched = len(aspath) == 1 && aspath[0] == member.asn } if matched { log.WithFields(log.Fields{ "Topic": "Policy", "Condition": "aspath length", "ASN": member.asn, "Position": member.postiion, }).Debug("condition matched") return true } } return false }
// compare neighbor ipaddress of this condition and source address of path // and, subsequent comparisons are skipped if that matches the conditions. // If NeighborList's length is zero, return true. func (c *NeighborCondition) evaluate(path *table.Path) bool { if len(c.NeighborList) == 0 { log.Debug("NeighborList doesn't have elements") return true } for _, neighbor := range c.NeighborList { cAddr := neighbor pAddr := path.GetSource().Address if pAddr.Equal(cAddr) { log.WithFields(log.Fields{ "Topic": "Policy", "NeighborAddress": pAddr.String(), }).Debug("neighbor matched") return true } } return false }
func (a *CommunityAction) apply(path *table.Path) *table.Path { list := a.Values switch a.action { case config.BGP_SET_COMMUNITY_OPTION_TYPE_ADD: path.SetCommunities(list, false) case config.BGP_SET_COMMUNITY_OPTION_TYPE_REMOVE: path.RemoveCommunities(list) case config.BGP_SET_COMMUNITY_OPTION_TYPE_REPLACE: path.SetCommunities(list, true) case config.BGP_SET_COMMUNITY_OPTION_TYPE_NULL: path.ClearCommunities() } return path }
func (peer *Peer) filterpath(path *table.Path) *table.Path { // special handling for RTC nlri // see comments in (*Destination).Calculate() if path != nil && path.GetRouteFamily() == bgp.RF_RTC_UC && !path.IsWithdraw { // if we already sent the same nlri, ignore this if peer.adjRibOut.Exists(path) { return nil } dst := peer.localRib.GetDestination(path) path = nil // we send a path even if it is not a best path for _, p := range dst.GetKnownPathList(peer.TableID()) { // just take care not to send back it if peer.ID() != p.GetSource().Address.String() { path = p break } } } if filterpath(peer, path) == nil { return nil } if !peer.isRouteServerClient() { path = path.Clone(path.IsWithdraw) path.UpdatePathAttrs(peer.fsm.gConf, peer.fsm.pConf) } options := &table.PolicyOptions{ Info: peer.fsm.peerInfo, } path = peer.policy.ApplyPolicy(peer.TableID(), table.POLICY_DIRECTION_EXPORT, path, options) // remove local-pref attribute // we should do this after applying export policy since policy may // set local-preference if path != nil && peer.fsm.pConf.Config.PeerType == config.PEER_TYPE_EXTERNAL { path.RemoveLocalPref() } return path }
func (a *MedAction) apply(path *table.Path) *table.Path { var err error switch a.action { case MED_ACTION_REPLACE: err = path.SetMed(a.Value, true) case MED_ACTION_ADD: err = path.SetMed(a.Value, false) case MED_ACTION_SUB: err = path.SetMed(a.Value, false) } if err != nil { log.WithFields(log.Fields{ "Topic": "Policy", "Type": "Med Action", }).Warn(err) } return path }
func (peer *Peer) filterpath(path, old *table.Path) *table.Path { // special handling for RTC nlri // see comments in (*Destination).Calculate() if path != nil && path.GetRouteFamily() == bgp.RF_RTC_UC && !path.IsWithdraw { // If we already sent the same nlri, send unnecessary // update. Fix this after the API change between table // and server packages. dst := peer.localRib.GetDestination(path) path = nil // we send a path even if it is not a best path for _, p := range dst.GetKnownPathList(peer.TableID()) { // just take care not to send back it if peer.ID() != p.GetSource().Address.String() { path = p break } } } // only allow vpnv4 and vpnv6 paths to be advertised to VRFed neighbors. // also check we can import this path using table.CanImportToVrf() // if we can, make it local path by calling (*Path).ToLocal() if path != nil && peer.fsm.pConf.Config.Vrf != "" { if f := path.GetRouteFamily(); f != bgp.RF_IPv4_VPN && f != bgp.RF_IPv6_VPN { return nil } vrf := peer.localRib.Vrfs[peer.fsm.pConf.Config.Vrf] if table.CanImportToVrf(vrf, path) { path = path.ToLocal() } else { return nil } } if path = filterpath(peer, path, old); path == nil { return nil } path = path.Clone(path.IsWithdraw) path.UpdatePathAttrs(peer.fsm.gConf, peer.fsm.pConf) options := &table.PolicyOptions{ Info: peer.fsm.peerInfo, } path = peer.policy.ApplyPolicy(peer.TableID(), table.POLICY_DIRECTION_EXPORT, path, options) // draft-uttaro-idr-bgp-persistence-02 // 4.3. Processing LLGR_STALE Routes // // The route SHOULD NOT be advertised to any neighbor from which the // Long-lived Graceful Restart Capability has not been received. The // exception is described in the Optional Partial Deployment // Procedure section (Section 4.7). Note that this requirement // implies that such routes should be withdrawn from any such neighbor. if path != nil && !path.IsWithdraw && !peer.isLLGREnabledFamily(path.GetRouteFamily()) && path.IsLLGRStale() { // we send unnecessary withdrawn even if we didn't // sent the route. path = path.Clone(true) } // remove local-pref attribute // we should do this after applying export policy since policy may // set local-preference if path != nil && !peer.isIBGPPeer() && !peer.isRouteServerClient() { path.RemoveLocalPref() } return path }
func path2data(path *table.Path) (map[string]interface{}, map[string]string) { fields := map[string]interface{}{ "ASPath": path.GetAsPath().String(), } if origin, err := path.GetOrigin(); err == nil { typ := "-" switch origin { case bgp.BGP_ORIGIN_ATTR_TYPE_IGP: typ = "i" case bgp.BGP_ORIGIN_ATTR_TYPE_EGP: typ = "e" case bgp.BGP_ORIGIN_ATTR_TYPE_INCOMPLETE: typ = "?" } fields["Origin"] = typ } if med, err := path.GetMed(); err == nil { fields["Med"] = med } var prefix, prefixLen string l := strings.Split(path.GetNlri().String(), "/") if len(l) == 2 { prefix = l[0] prefixLen = l[1] } tags := map[string]string{ "PeerAddress": path.GetSource().Address.String(), "PeerAS": fmt.Sprintf("%v", path.GetSource().AS), "Prefix": prefix, "PrefixLen": prefixLen, "NextHop": path.GetNexthop().String(), "OriginAS": fmt.Sprintf("%v", path.GetSourceAs()), "Timestamp": path.GetTimestamp().String(), } return fields, tags }
func path2data(path *table.Path) (map[string]interface{}, map[string]string) { fields := map[string]interface{}{ "RouterID": path.GetSource().ID, } if asPath := path.GetAsPath(); asPath != nil { fields["ASPath"] = asPath.String() } if origin, err := path.GetOrigin(); err == nil { typ := "-" switch origin { case bgp.BGP_ORIGIN_ATTR_TYPE_IGP: typ = "i" case bgp.BGP_ORIGIN_ATTR_TYPE_EGP: typ = "e" case bgp.BGP_ORIGIN_ATTR_TYPE_INCOMPLETE: typ = "?" } fields["Origin"] = typ } if med, err := path.GetMed(); err == nil { fields["Med"] = med } tags := map[string]string{ "PeerAddress": path.GetSource().Address.String(), "PeerAS": fmt.Sprintf("%v", path.GetSource().AS), "Timestamp": path.GetTimestamp().String(), } if nexthop := path.GetNexthop(); len(nexthop) > 0 { fields["NextHop"] = nexthop.String() } if originAS := path.GetSourceAs(); originAS != 0 { fields["OriginAS"] = fmt.Sprintf("%v", originAS) } if err := bgp.FlatUpdate(tags, path.GetNlri().Flat()); err != nil { log.Error(err) } for _, p := range path.GetPathAttrs() { if err := bgp.FlatUpdate(tags, p.Flat()); err != nil { log.Error(err) } } return fields, tags }
//modrib receives route updates from BGP server and adds the endpoint func (self *OfnetBgp) modRib(path *table.Path) error { var nlri bgp.AddrPrefixInterface var nextHop string var macAddrStr string var portNo uint32 nlri = path.GetNlri() nextHop = path.GetNexthop().String() if nextHop == "0.0.0.0" { return nil } if nlri == nil { return fmt.Errorf("no nlri") } endpointIPNet, _ := netlink.ParseIPNet(nlri.String()) log.Infof("Bgp Rib Received endpoint update for path %s", path.String()) //check if bgp published a route local to the host epid := self.agent.getEndpointIdByIpVrf(endpointIPNet.IP.Mask(endpointIPNet.Mask), "default") //Check if the route is local if nextHop == self.routerIP { log.Debugf("This is a local route skipping endpoint create! ") return nil } else if ep := self.agent.getEndpointByID(epid); ep != nil { if ep.EndpointType != "external" { log.Debugf("Endpoint was learnt via internal protocol. skipping update! ") return nil } } nhEpid := self.agent.getEndpointIdByIpVrf(net.ParseIP(nextHop), "default") if ep := self.agent.getEndpointByID(nhEpid); ep == nil { //the nexthop is not the directly connected eBgp peer macAddrStr = "" portNo = 0 } else { macAddrStr = ep.MacAddrStr portNo = ep.PortNo } ipmask := net.ParseIP("255.255.255.255").Mask(endpointIPNet.Mask) if path.IsWithdraw != true { epreg := &OfnetEndpoint{ EndpointID: epid, EndpointType: "external", IpAddr: endpointIPNet.IP, IpMask: ipmask, Vrf: "default", // FIXME set VRF correctly MacAddrStr: macAddrStr, Vlan: 1, OriginatorIp: self.agent.localIp, PortNo: portNo, Timestamp: time.Now(), } // Install the endpoint in datapath // First, add the endpoint to local routing table self.agent.endpointDb.Set(epreg.EndpointID, epreg) err := self.agent.datapath.AddEndpoint(epreg) if err != nil { log.Errorf("Error adding endpoint: {%+v}. Err: %v", epreg, err) return err } } else { log.Info("Received route withdraw from BGP for ", endpointIPNet) endpoint := self.agent.getEndpointByIpVrf(endpointIPNet.IP, "default") if endpoint != nil { self.agent.datapath.RemoveEndpoint(endpoint) self.agent.endpointDb.Remove(endpoint.EndpointID) } } return nil }
// compare extended community in the message's attribute with // the one in the condition. func (c *ExtCommunityCondition) evaluate(path *table.Path) bool { makeAs4Str := func(ec *ExtCommunityElement) string { t := ec.ecType str := fmt.Sprintf("%d", ec.localAdmin) switch t { case bgp.EC_TYPE_TRANSITIVE_TWO_OCTET_AS_SPECIFIC: str = fmt.Sprintf("%d:%s", ec.globalAdmin.(uint16), str) case bgp.EC_TYPE_TRANSITIVE_IP4_SPECIFIC: str = fmt.Sprintf("%s:%s", ec.globalAdmin.(net.IP).String(), str) case bgp.EC_TYPE_TRANSITIVE_FOUR_OCTET_AS_SPECIFIC: ga := ec.globalAdmin.(uint32) upper := strconv.FormatUint(uint64(ga&0xFFFF0000>>16), 10) lower := strconv.FormatUint(uint64(ga&0x0000FFFF), 10) str = fmt.Sprintf("%s.%s:%s", upper, lower, str) } return str } makeTypeSubStr := func(st bgp.ExtendedCommunityAttrSubType) string { subStr := "" switch st { case bgp.EC_SUBTYPE_ROUTE_TARGET: subStr = "RT" case bgp.EC_SUBTYPE_ROUTE_ORIGIN: subStr = "SoO" } return subStr } eCommunities := path.GetExtCommunities() if len(eCommunities) == 0 || c == nil { return false } matched := false matchStr := "" for _, member := range c.ExtCommunityList { for _, eCommunity := range eCommunities { ec := eCommunity.(bgp.ExtendedCommunityInterface) t, st := ec.GetTypes() if member.isRegExp { ecString := fmt.Sprintf("%s:%s", makeTypeSubStr(st), ec.String()) if member.regExp.MatchString(ecString) { matched = true log.WithFields(log.Fields{ "Topic": "Policy", "RegExp": member.regExp.String(), }).Debug("extended community regexp used") matchStr = ec.String() break } } else if member.ecType == t && member.ecSubType == st { if makeAs4Str(member) == ec.String() { matched = true matchStr = ec.String() break } } } if matched { log.WithFields(log.Fields{ "Topic": "Policy", "Condition": "Extended Community", "Extended Community": matchStr, }).Debug("condition matched") return true } } return false }
// compare community in the message's attribute with // the one in the condition. func (c *CommunityCondition) evaluate(path *table.Path) bool { communities := path.GetCommunities() if len(communities) == 0 { return false } makeStr := func(c uint32) string { upper := strconv.FormatUint(uint64(c&0xFFFF0000>>16), 10) lower := strconv.FormatUint(uint64(c&0x0000FFFF), 10) return upper + ":" + lower } var strCommunities []string = nil matched := false idx := -1 for _, member := range c.CommunityList { if member.isRegExp { if strCommunities == nil { // create community string. strCommunities = make([]string, len(communities)) for i, c := range communities { strCommunities[i] = makeStr(c) } } for i, c := range strCommunities { if member.communityRegExp.MatchString(c) { matched = true idx = i log.WithFields(log.Fields{ "Topic": "Policy", "RegExp": member.communityRegExp.String(), }).Debug("community regexp used") break } } } else { for i, c := range communities { if c == member.community { matched = true idx = i break } } } if matched { log.WithFields(log.Fields{ "Topic": "Policy", "Condition": "Community", "Community": makeStr(communities[idx]), }).Debug("condition matched") return true } } return false }
func ToPathApi(path *table.Path) *Path { nlri := path.GetNlri() n, _ := nlri.Serialize() family := uint32(bgp.AfiSafiToRouteFamily(nlri.AFI(), nlri.SAFI())) pattrs := func(arg []bgp.PathAttributeInterface) [][]byte { ret := make([][]byte, 0, len(arg)) for _, a := range arg { aa, _ := a.Serialize() ret = append(ret, aa) } return ret }(path.GetPathAttrs()) return &Path{ Nlri: n, Pattrs: pattrs, Age: path.GetTimestamp().Unix(), IsWithdraw: path.IsWithdraw, Validation: int32(path.Validation().ToInt()), Filtered: path.Filtered("") == table.POLICY_DIRECTION_IN, Family: family, SourceAsn: path.GetSource().AS, SourceId: path.GetSource().ID.String(), NeighborIp: path.GetSource().Address.String(), Stale: path.IsStale(), IsFromExternal: path.IsFromExternal(), } }