diff options
-rw-r--r-- | packet/bgp.go | 37 | ||||
-rw-r--r-- | policy/policy.go | 204 | ||||
-rw-r--r-- | table/path.go | 11 |
3 files changed, 252 insertions, 0 deletions
diff --git a/packet/bgp.go b/packet/bgp.go index b95d48f3..3ff5615e 100644 --- a/packet/bgp.go +++ b/packet/bgp.go @@ -3023,6 +3023,7 @@ func NewPathAttributeMpUnreachNLRI(nlri []AddrPrefixInterface) *PathAttributeMpU type ExtendedCommunityInterface interface { Serialize() ([]byte, error) String() string + GetTypes() (ExtendedCommunityAttrType, ExtendedCommunityAttrSubType) ToApiStruct() *api.ExtendedCommunity } @@ -3050,6 +3051,14 @@ func (e *TwoOctetAsSpecificExtended) String() string { return fmt.Sprintf("%d:%d", e.AS, e.LocalAdmin) } +func (e *TwoOctetAsSpecificExtended) GetTypes() (ExtendedCommunityAttrType, ExtendedCommunityAttrSubType) { + t := EC_TYPE_TRANSITIVE_TWO_OCTET_AS_SPECIFIC + if !e.IsTransitive { + t = EC_TYPE_NON_TRANSITIVE_TWO_OCTET_AS_SPECIFIC + } + return t, e.SubType +} + func (e *TwoOctetAsSpecificExtended) ToApiStruct() *api.ExtendedCommunity { return &api.ExtendedCommunity{ Type: api.EXTENDED_COMMUNITIE_TYPE_TWO_OCTET_AS_SPECIFIC, @@ -3093,6 +3102,14 @@ func (e *IPv4AddressSpecificExtended) String() string { return fmt.Sprintf("%s:%d", e.IPv4.String(), e.LocalAdmin) } +func (e *IPv4AddressSpecificExtended) GetTypes() (ExtendedCommunityAttrType, ExtendedCommunityAttrSubType) { + t := EC_TYPE_TRANSITIVE_IP4_SPECIFIC + if !e.IsTransitive { + t = EC_TYPE_NON_TRANSITIVE_IP4_SPECIFIC + } + return t, e.SubType +} + func (e *IPv4AddressSpecificExtended) ToApiStruct() *api.ExtendedCommunity { return &api.ExtendedCommunity{ Type: api.EXTENDED_COMMUNITIE_TYPE_IP4_SPECIFIC, @@ -3131,6 +3148,14 @@ func (e *FourOctetAsSpecificExtended) String() string { return fmt.Sprintf("%d.%d:%d", asUpper, asLower, e.LocalAdmin) } +func (e *FourOctetAsSpecificExtended) GetTypes() (ExtendedCommunityAttrType, ExtendedCommunityAttrSubType) { + t := EC_TYPE_TRANSITIVE_FOUR_OCTET_AS_SPECIFIC + if !e.IsTransitive { + t = EC_TYPE_NON_TRANSITIVE_FOUR_OCTET_AS_SPECIFIC + } + return t, e.SubType +} + func (e *FourOctetAsSpecificExtended) ToApiStruct() *api.ExtendedCommunity { return &api.ExtendedCommunity{ Type: api.EXTENDED_COMMUNITIE_TYPE_FOUR_OCTET_AS_SPECIFIC, @@ -3259,6 +3284,14 @@ func (e *OpaqueExtended) String() string { return e.Value.String() } +func (e *OpaqueExtended) GetTypes() (ExtendedCommunityAttrType, ExtendedCommunityAttrSubType) { + t := EC_TYPE_TRANSITIVE_OPAQUE + if !e.IsTransitive { + t = EC_TYPE_NON_TRANSITIVE_OPAQUE + } + return t, ExtendedCommunityAttrSubType(0xFF) +} + func (e *OpaqueExtended) ToApiStruct() *api.ExtendedCommunity { return &api.ExtendedCommunity{ Type: api.EXTENDED_COMMUNITIE_TYPE_OPAQUE, @@ -3290,6 +3323,10 @@ func (e *UnknownExtended) String() string { return fmt.Sprintf("%d", v) } +func (e *UnknownExtended) GetTypes() (ExtendedCommunityAttrType, ExtendedCommunityAttrSubType) { + return ExtendedCommunityAttrType(0xFF), ExtendedCommunityAttrSubType(0xFF) +} + func (e *UnknownExtended) ToApiStruct() *api.ExtendedCommunity { return &api.ExtendedCommunity{} } diff --git a/policy/policy.go b/policy/policy.go index 300fcadf..b35e765f 100644 --- a/policy/policy.go +++ b/policy/policy.go @@ -103,6 +103,13 @@ func NewPolicy(pd config.PolicyDefinition, ds config.DefinedSets) *Policy { conditions = append(conditions, cc) } + // ExtendedCommunityCondition + extCommunitySetName := statement.Conditions.BgpConditions.MatchExtCommunitySet + ecc := NewExtCommunityCondition(extCommunitySetName, ds.BgpDefinedSets.ExtCommunitySetList) + if ecc != nil { + conditions = append(conditions, ecc) + } + // routing action ra := NewRoutingAction(statement.Actions) @@ -689,6 +696,203 @@ func (c *CommunityCondition) evaluate(path *table.Path) bool { return false } +type ExtCommunityCondition struct { + DefaultCondition + ExtCommunityList []*ExtCommunityElement +} + +type ExtCommunityElement struct { + ecType bgp.ExtendedCommunityAttrType + ecSubType bgp.ExtendedCommunityAttrSubType + globalAdmin interface{} + localAdmin uint32 + comStr string + isRegExp bool + regExp *regexp.Regexp +} + +func NewExtCommunityCondition(extComSetName string, defExtComSetList []config.ExtCommunitySet) *ExtCommunityCondition { + extCommunityList := make([]*ExtCommunityElement, 0) + for _, extComSet := range defExtComSetList { + if extComSet.ExtCommunitySetName == extComSetName { + for _, c := range extComSet.ExtCommunityMembers { + matchAll := false + e := &ExtCommunityElement{ + isRegExp: false, + } + matchType, val := getECommunitySubType(c) + if !matchType { + log.WithFields(log.Fields{ + "Topic": "Policy", + "Type": "Community Condition", + }).Error("failed to parse the sub type %s.", c) + return nil + } + switch val[1] { + case "RT": + e.ecSubType = bgp.EC_SUBTYPE_ROUTE_TARGET + case "SoO": + e.ecSubType = bgp.EC_SUBTYPE_ROUTE_ORIGIN + default: + e.ecSubType = bgp.ExtendedCommunityAttrSubType(0xFF) + } + + if matchVal, elem := getECommunityValue(val[2]); matchVal { + if matchElem, ecType, gAdmin := getECommunityElem(elem[1]); matchElem { + e.ecType = ecType + e.globalAdmin = gAdmin + lAdmin, err := strconv.ParseUint(elem[2], 10, 32) + if err != nil { + log.WithFields(log.Fields{ + "Topic": "Policy", + "Type": "Extended Community Condition", + }).Errorf("failed to parse the local administrator %d.", elem[2]) + return nil + } + e.localAdmin = uint32(lAdmin) + matchAll = true + } + } + if !matchAll{ + e.isRegExp = true + reg, err := regexp.Compile(val[2]) + if err != nil { + log.WithFields(log.Fields{ + "Topic": "Policy", + "Type": "Community Condition", + }).Errorf("Regular expression can't be compiled %d.", val[2]) + return nil + } + e.regExp = reg + } + extCommunityList = append(extCommunityList, e) + } + ce := &ExtCommunityCondition{ + ExtCommunityList: extCommunityList, + } + return ce + } + } + return nil +} + +func getECommunitySubType(eComStr string) (bool, []string) { + regSubType, _ := regexp.Compile("^(RT|SoO):(.*)$") + if regSubType.MatchString(eComStr) { + eComVal := regSubType.FindStringSubmatch(eComStr) + return true, eComVal + } + return false, nil +} + +func getECommunityValue(eComVal string) (bool, []string) { + regVal, _ := regexp.Compile("^([0-9\\.]+):([0-9]+)$") + if regVal.MatchString(eComVal) { + eComElem := regVal.FindStringSubmatch(eComVal) + return true, eComElem + } + return false, nil +} + +func getECommunityElem(gAdmin string) (bool, bgp.ExtendedCommunityAttrType, interface{}) { + addr := net.ParseIP(gAdmin) + if addr.To4() != nil { + return true, bgp.EC_TYPE_TRANSITIVE_IP4_SPECIFIC, addr + } + regAs, _ := regexp.Compile("^([0-9]+)$") + if regAs.MatchString(gAdmin) { + as, err := strconv.ParseUint(gAdmin, 10, 16) + if err != nil { + log.WithFields(log.Fields{ + "Topic": "Policy", + "Type": "Extended Community Condition", + }).Errorf("failed to parse the global administrator %d.", gAdmin) + } + return true, bgp.EC_TYPE_TRANSITIVE_TWO_OCTET_AS_SPECIFIC, uint16(as) + } + regAs4, _ := regexp.Compile("^([0-9]+).([0-9]+)$") + if regAs4.MatchString(gAdmin) { + as4Elem := regAs4.FindStringSubmatch(gAdmin) + highAs, errHigh := strconv.ParseUint(as4Elem[1], 10, 16) + lowAs, errLow := strconv.ParseUint(as4Elem[2], 10, 16) + if errHigh != nil || errLow != nil { + log.WithFields(log.Fields{ + "Topic": "Policy", + "Type": "Extended Community Condition", + }).Errorf("failed to parse the global administrator %d.", gAdmin) + } + return true, bgp.EC_TYPE_TRANSITIVE_FOUR_OCTET_AS_SPECIFIC, uint32(highAs<<16 | lowAs) + } + return false, bgp.ExtendedCommunityAttrType(0xFF), nil +} + +// compare extended community in the message's attribute with +// the one in the condition. +func (c *ExtCommunityCondition) evaluate(path *table.Path) bool { + + makeStr := 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:%d", ec.globalAdmin.(uint16), str) + case bgp.EC_TYPE_TRANSITIVE_IP4_SPECIFIC: + str = fmt.Sprintf("%s:%d", 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("%d.%d:%s", upper, lower, str) + } + return str + } + + eCommunities := path.GetExtCommunities() + if len(eCommunities) == 0 { + return false + } + + matched := false + matchStr := "" + + for _, member := range c.ExtCommunityList { + for _, eCommunity := range eCommunities { + ec := eCommunity.(bgp.ExtendedCommunityInterface) + t, st := ec.GetTypes() + if member.ecType != t || member.ecSubType != st { + continue + } + if member.isRegExp { + if member.regExp.MatchString(ec.String()) { + matched = true + log.WithFields(log.Fields{ + "Topic": "Policy", + "RegExp": member.regExp.String(), + }).Debug("extended community regexp used") + matchStr = ec.String() + break + } + } else { + if makeStr(member) == ec.String() { + matched = true + matchStr = ec.String() + break + } + } + } + if matched { + log.WithFields(log.Fields{ + "Topic": "Policy", + "Condition": "Community", + "Community": matchStr, + }).Debug("condition matched") + + return true + } + } + return false +} + type Action interface { apply(*table.Path) *table.Path } diff --git a/table/path.go b/table/path.go index 302847f8..75c67a83 100644 --- a/table/path.go +++ b/table/path.go @@ -438,6 +438,17 @@ func (path *Path) ClearCommunities() { } } +func (path *Path) GetExtCommunities() []interface{} { + eCommunityList := make([]interface{}, 0) + if _, attr := path.getPathAttr(bgp.BGP_ATTR_TYPE_EXTENDED_COMMUNITIES); attr != nil { + eCommunities := attr.(*bgp.PathAttributeExtendedCommunities).Value + for _, eCommunity := range eCommunities { + eCommunityList = append(eCommunityList, eCommunity) + } + } + return eCommunityList +} + func (path *Path) GetMed() (uint32, error) { _, attr := path.getPathAttr(bgp.BGP_ATTR_TYPE_MULTI_EXIT_DISC) if attr == nil { |