diff options
Diffstat (limited to 'pkg/packet/bgp/validate.go')
-rw-r--r-- | pkg/packet/bgp/validate.go | 337 |
1 files changed, 337 insertions, 0 deletions
diff --git a/pkg/packet/bgp/validate.go b/pkg/packet/bgp/validate.go new file mode 100644 index 00000000..60cf26e4 --- /dev/null +++ b/pkg/packet/bgp/validate.go @@ -0,0 +1,337 @@ +package bgp + +import ( + "encoding/binary" + "fmt" + "math" + "net" + "strconv" +) + +// Validator for BGPUpdate +func ValidateUpdateMsg(m *BGPUpdate, rfs map[RouteFamily]BGPAddPathMode, isEBGP bool, isConfed bool) (bool, error) { + var strongestError error + + eCode := uint8(BGP_ERROR_UPDATE_MESSAGE_ERROR) + eSubCodeAttrList := uint8(BGP_ERROR_SUB_MALFORMED_ATTRIBUTE_LIST) + eSubCodeMissing := uint8(BGP_ERROR_SUB_MISSING_WELL_KNOWN_ATTRIBUTE) + + if len(m.NLRI) > 0 || len(m.WithdrawnRoutes) > 0 { + if _, ok := rfs[RF_IPv4_UC]; !ok { + return false, NewMessageError(0, 0, nil, fmt.Sprintf("Address-family rf %d not available for session", RF_IPv4_UC)) + } + } + + seen := make(map[BGPAttrType]PathAttributeInterface) + newAttrs := make([]PathAttributeInterface, 0, len(seen)) + // check path attribute + for _, a := range m.PathAttributes { + // check duplication + if _, ok := seen[a.GetType()]; !ok { + seen[a.GetType()] = a + newAttrs = append(newAttrs, a) + //check specific path attribute + ok, err := ValidateAttribute(a, rfs, isEBGP, isConfed) + if !ok { + if err.(*MessageError).ErrorHandling == ERROR_HANDLING_SESSION_RESET { + return false, err + } else if err.(*MessageError).Stronger(strongestError) { + strongestError = err + } + } + } else if a.GetType() == BGP_ATTR_TYPE_MP_REACH_NLRI || a.GetType() == BGP_ATTR_TYPE_MP_UNREACH_NLRI { + eMsg := "the path attribute apears twice. Type : " + strconv.Itoa(int(a.GetType())) + return false, NewMessageError(eCode, eSubCodeAttrList, nil, eMsg) + } else { + eMsg := "the path attribute apears twice. Type : " + strconv.Itoa(int(a.GetType())) + e := NewMessageErrorWithErrorHandling(eCode, eSubCodeAttrList, nil, ERROR_HANDLING_ATTRIBUTE_DISCARD, nil, eMsg) + if e.(*MessageError).Stronger(strongestError) { + strongestError = e + } + } + } + m.PathAttributes = newAttrs + + if _, ok := seen[BGP_ATTR_TYPE_MP_REACH_NLRI]; ok || len(m.NLRI) > 0 { + // check the existence of well-known mandatory attributes + exist := func(attrs []BGPAttrType) (bool, BGPAttrType) { + for _, attr := range attrs { + _, ok := seen[attr] + if !ok { + return false, attr + } + } + return true, 0 + } + mandatory := []BGPAttrType{BGP_ATTR_TYPE_ORIGIN, BGP_ATTR_TYPE_AS_PATH} + if len(m.NLRI) > 0 { + mandatory = append(mandatory, BGP_ATTR_TYPE_NEXT_HOP) + } + if ok, t := exist(mandatory); !ok { + eMsg := "well-known mandatory attributes are not present. type : " + strconv.Itoa(int(t)) + data := []byte{byte(t)} + e := NewMessageErrorWithErrorHandling(eCode, eSubCodeMissing, data, ERROR_HANDLING_TREAT_AS_WITHDRAW, nil, eMsg) + if e.(*MessageError).Stronger(strongestError) { + strongestError = e + } + } + } + + return strongestError == nil, strongestError +} + +func ValidateAttribute(a PathAttributeInterface, rfs map[RouteFamily]BGPAddPathMode, isEBGP bool, isConfed bool) (bool, error) { + var strongestError error + + eCode := uint8(BGP_ERROR_UPDATE_MESSAGE_ERROR) + eSubCodeBadOrigin := uint8(BGP_ERROR_SUB_INVALID_ORIGIN_ATTRIBUTE) + eSubCodeBadNextHop := uint8(BGP_ERROR_SUB_INVALID_NEXT_HOP_ATTRIBUTE) + eSubCodeUnknown := uint8(BGP_ERROR_SUB_UNRECOGNIZED_WELL_KNOWN_ATTRIBUTE) + eSubCodeMalformedAspath := uint8(BGP_ERROR_SUB_MALFORMED_AS_PATH) + + checkPrefix := func(l []AddrPrefixInterface) error { + for _, prefix := range l { + rf := AfiSafiToRouteFamily(prefix.AFI(), prefix.SAFI()) + if _, ok := rfs[rf]; !ok { + return NewMessageError(0, 0, nil, fmt.Sprintf("Address-family %s not available for this session", rf)) + } + switch rf { + case RF_FS_IPv4_UC, RF_FS_IPv6_UC, RF_FS_IPv4_VPN, RF_FS_IPv6_VPN, RF_FS_L2_VPN: + t := BGPFlowSpecType(0) + value := make([]FlowSpecComponentInterface, 0) + switch rf { + case RF_FS_IPv4_UC: + value = prefix.(*FlowSpecIPv4Unicast).Value + case RF_FS_IPv6_UC: + value = prefix.(*FlowSpecIPv6Unicast).Value + case RF_FS_IPv4_VPN: + value = prefix.(*FlowSpecIPv4VPN).Value + case RF_FS_IPv6_VPN: + value = prefix.(*FlowSpecIPv6VPN).Value + case RF_FS_L2_VPN: + value = prefix.(*FlowSpecL2VPN).Value + } + for _, v := range value { + if v.Type() <= t { + return NewMessageError(0, 0, nil, fmt.Sprintf("%s nlri violate strict type ordering", rf)) + } + t = v.Type() + } + } + } + return nil + } + + switch p := a.(type) { + case *PathAttributeMpUnreachNLRI: + rf := AfiSafiToRouteFamily(p.AFI, p.SAFI) + if _, ok := rfs[rf]; !ok { + return false, NewMessageError(0, 0, nil, fmt.Sprintf("Address-family rf %d not available for session", rf)) + } + if err := checkPrefix(p.Value); err != nil { + return false, err + } + case *PathAttributeMpReachNLRI: + rf := AfiSafiToRouteFamily(p.AFI, p.SAFI) + if _, ok := rfs[rf]; !ok { + return false, NewMessageError(0, 0, nil, fmt.Sprintf("Address-family rf %d not available for session", rf)) + } + if err := checkPrefix(p.Value); err != nil { + return false, err + } + case *PathAttributeOrigin: + v := uint8(p.Value) + if v != BGP_ORIGIN_ATTR_TYPE_IGP && + v != BGP_ORIGIN_ATTR_TYPE_EGP && + v != BGP_ORIGIN_ATTR_TYPE_INCOMPLETE { + data, _ := a.Serialize() + eMsg := "invalid origin attribute. value : " + strconv.Itoa(int(v)) + e := NewMessageErrorWithErrorHandling(eCode, eSubCodeBadOrigin, data, getErrorHandlingFromPathAttribute(p.GetType()), nil, eMsg) + if e.(*MessageError).Stronger(strongestError) { + strongestError = e + } + } + case *PathAttributeNextHop: + + isZero := func(ip net.IP) bool { + res := ip[0] & 0xff + return res == 0x00 + } + + isClassDorE := func(ip net.IP) bool { + res := ip[0] & 0xe0 + return res == 0xe0 + } + + //check IP address represents host address + if p.Value.IsLoopback() || isZero(p.Value) || isClassDorE(p.Value) { + eMsg := "invalid nexthop address" + data, _ := a.Serialize() + e := NewMessageErrorWithErrorHandling(eCode, eSubCodeBadNextHop, data, getErrorHandlingFromPathAttribute(p.GetType()), nil, eMsg) + if e.(*MessageError).Stronger(strongestError) { + strongestError = e + } + } + case *PathAttributeAsPath: + if isEBGP { + if isConfed { + if segType := p.Value[0].GetType(); segType != BGP_ASPATH_ATTR_TYPE_CONFED_SEQ { + return false, NewMessageError(eCode, eSubCodeMalformedAspath, nil, fmt.Sprintf("segment type is not confederation seq (%d)", segType)) + } + } else { + for _, param := range p.Value { + segType := param.GetType() + switch segType { + case BGP_ASPATH_ATTR_TYPE_CONFED_SET, BGP_ASPATH_ATTR_TYPE_CONFED_SEQ: + err := NewMessageErrorWithErrorHandling( + eCode, eSubCodeMalformedAspath, nil, getErrorHandlingFromPathAttribute(p.GetType()), nil, fmt.Sprintf("segment type confederation(%d) found", segType)) + if err.(*MessageError).Stronger(strongestError) { + strongestError = err + } + } + } + } + } + case *PathAttributeLargeCommunities: + uniq := make([]*LargeCommunity, 0, len(p.Values)) + for _, x := range p.Values { + found := false + for _, y := range uniq { + if x.String() == y.String() { + found = true + break + } + } + if !found { + uniq = append(uniq, x) + } + } + p.Values = uniq + + case *PathAttributeUnknown: + if p.GetFlags()&BGP_ATTR_FLAG_OPTIONAL == 0 { + eMsg := fmt.Sprintf("unrecognized well-known attribute %s", p.GetType()) + data, _ := a.Serialize() + return false, NewMessageError(eCode, eSubCodeUnknown, data, eMsg) + } + } + + return strongestError == nil, strongestError +} + +// validator for PathAttribute +func validatePathAttributeFlags(t BGPAttrType, flags BGPAttrFlag) string { + + /* + * RFC 4271 P.17 For well-known attributes, the Transitive bit MUST be set to 1. + */ + if flags&BGP_ATTR_FLAG_OPTIONAL == 0 && flags&BGP_ATTR_FLAG_TRANSITIVE == 0 { + eMsg := fmt.Sprintf("well-known attribute %s must have transitive flag 1", t) + return eMsg + } + /* + * RFC 4271 P.17 For well-known attributes and for optional non-transitive attributes, + * the Partial bit MUST be set to 0. + */ + if flags&BGP_ATTR_FLAG_OPTIONAL == 0 && flags&BGP_ATTR_FLAG_PARTIAL != 0 { + eMsg := fmt.Sprintf("well-known attribute %s must have partial bit 0", t) + return eMsg + } + if flags&BGP_ATTR_FLAG_OPTIONAL != 0 && flags&BGP_ATTR_FLAG_TRANSITIVE == 0 && flags&BGP_ATTR_FLAG_PARTIAL != 0 { + eMsg := fmt.Sprintf("optional non-transitive attribute %s must have partial bit 0", t) + return eMsg + } + + // check flags are correct + if f, ok := PathAttrFlags[t]; ok { + if f != flags & ^BGP_ATTR_FLAG_EXTENDED_LENGTH & ^BGP_ATTR_FLAG_PARTIAL { + eMsg := fmt.Sprintf("flags are invalid. attribute type: %s, expect: %s, actual: %s", t, f, flags) + return eMsg + } + } + return "" +} + +func validateAsPathValueBytes(data []byte) (bool, error) { + eCode := uint8(BGP_ERROR_UPDATE_MESSAGE_ERROR) + eSubCode := uint8(BGP_ERROR_SUB_MALFORMED_AS_PATH) + if len(data)%2 != 0 { + return false, NewMessageError(eCode, eSubCode, nil, "AS PATH length is not odd") + } + + tryParse := func(data []byte, use4byte bool) (bool, error) { + for len(data) > 0 { + if len(data) < 2 { + return false, NewMessageError(eCode, eSubCode, nil, "AS PATH header is short") + } + segType := data[0] + if segType == 0 || segType > 4 { + return false, NewMessageError(eCode, eSubCode, nil, "unknown AS_PATH seg type") + } + asNum := data[1] + data = data[2:] + if asNum == 0 || int(asNum) > math.MaxUint8 { + return false, NewMessageError(eCode, eSubCode, nil, "AS PATH the number of AS is incorrect") + } + segLength := int(asNum) + if use4byte { + segLength *= 4 + } else { + segLength *= 2 + } + if int(segLength) > len(data) { + return false, NewMessageError(eCode, eSubCode, nil, "seg length is short") + } + data = data[segLength:] + } + return true, nil + } + _, err := tryParse(data, true) + if err == nil { + return true, nil + } + + _, err = tryParse(data, false) + if err == nil { + return false, nil + } + return false, NewMessageError(eCode, eSubCode, nil, "can't parse AS_PATH") +} + +func ValidateBGPMessage(m *BGPMessage) error { + if m.Header.Len > BGP_MAX_MESSAGE_LENGTH { + buf := make([]byte, 2) + binary.BigEndian.PutUint16(buf, m.Header.Len) + return NewMessageError(BGP_ERROR_MESSAGE_HEADER_ERROR, BGP_ERROR_SUB_BAD_MESSAGE_LENGTH, buf, "too long length") + } + + return nil +} + +func ValidateOpenMsg(m *BGPOpen, expectedAS uint32) (uint32, error) { + if m.Version != 4 { + return 0, NewMessageError(BGP_ERROR_OPEN_MESSAGE_ERROR, BGP_ERROR_SUB_UNSUPPORTED_VERSION_NUMBER, nil, fmt.Sprintf("unsupported version %d", m.Version)) + } + + as := uint32(m.MyAS) + for _, p := range m.OptParams { + paramCap, y := p.(*OptionParameterCapability) + if !y { + continue + } + for _, c := range paramCap.Capability { + if c.Code() == BGP_CAP_FOUR_OCTET_AS_NUMBER { + cap := c.(*CapFourOctetASNumber) + as = cap.CapValue + } + } + } + if expectedAS != 0 && as != expectedAS { + return 0, NewMessageError(BGP_ERROR_OPEN_MESSAGE_ERROR, BGP_ERROR_SUB_BAD_PEER_AS, nil, fmt.Sprintf("as number mismatch expected %d, received %d", expectedAS, as)) + } + + if m.HoldTime < 3 && m.HoldTime != 0 { + return 0, NewMessageError(BGP_ERROR_OPEN_MESSAGE_ERROR, BGP_ERROR_SUB_UNACCEPTABLE_HOLD_TIME, nil, fmt.Sprintf("unacceptable hold time %d", m.HoldTime)) + } + return as, nil +} |