diff options
Diffstat (limited to 'packet/bgp/rtr.go')
-rw-r--r-- | packet/bgp/rtr.go | 392 |
1 files changed, 392 insertions, 0 deletions
diff --git a/packet/bgp/rtr.go b/packet/bgp/rtr.go new file mode 100644 index 00000000..30657e15 --- /dev/null +++ b/packet/bgp/rtr.go @@ -0,0 +1,392 @@ +// Copyright (C) 2015 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 bgp + +import ( + "encoding/binary" + "fmt" + "net" +) + +const ( + RPKI_DEFAULT_PORT = 323 +) + +const ( + RTR_SERIAL_NOTIFY = iota + RTR_SERIAL_QUERY + RTR_RESET_QUERY + RTR_CACHE_RESPONSE + RTR_IPV4_PREFIX + _ + RTR_IPV6_PREFIX + RTR_END_OF_DATA + RTR_CACHE_RESET + _ + RTR_ERROR_REPORT +) + +const ( + RTR_SERIAL_NOTIFY_LEN = 12 + RTR_SERIAL_QUERY_LEN = 12 + RTR_RESET_QUERY_LEN = 8 + RTR_CACHE_RESPONSE_LEN = 8 + RTR_IPV4_PREFIX_LEN = 20 + RTR_IPV6_PREFIX_LEN = 32 + RTR_END_OF_DATA_LEN = 12 + RTR_CACHE_RESET_LEN = 8 + RTR_MIN_LEN = 8 + RTR_ERROR_REPORT_ERR_PDU_LEN = 4 + RTR_ERROR_REPORT_ERR_TEXT_LEN = 4 +) + +const ( + WITHDRAWAL uint8 = iota + ANNOUNCEMENT +) + +const ( + CORRUPT_DATA uint16 = iota + INTERNAL_ERROR + NO_DATA_AVAILABLE + INVALID_REQUEST + UNSUPPORTED_PROTOCOL_VERSION + UNSUPPORTED_PDU_TYPE + WITHDRAWAL_OF_UNKNOWN_RECORD + DUPLICATE_ANNOUNCEMENT_RECORD +) + +type RTRMessage interface { + DecodeFromBytes([]byte) error + Serialize() ([]byte, error) +} + +type RTRCommon struct { + Version uint8 + Type uint8 + SessionID uint16 + Len uint32 + SerialNumber uint32 +} + +func (m *RTRCommon) DecodeFromBytes(data []byte) error { + m.Version = data[0] + m.Type = data[1] + m.SessionID = binary.BigEndian.Uint16(data[2:4]) + m.Len = binary.BigEndian.Uint32(data[4:8]) + m.SerialNumber = binary.BigEndian.Uint32(data[8:12]) + return nil +} + +func (m *RTRCommon) Serialize() ([]byte, error) { + data := make([]byte, m.Len) + data[0] = m.Version + data[1] = m.Type + binary.BigEndian.PutUint16(data[2:4], m.SessionID) + binary.BigEndian.PutUint32(data[4:8], m.Len) + binary.BigEndian.PutUint32(data[8:12], m.SerialNumber) + return data, nil +} + +type RTRSerialNotify struct { + RTRCommon +} + +func NewRTRSerialNotify(id uint16, sn uint32) *RTRSerialNotify { + return &RTRSerialNotify{ + RTRCommon{ + Type: RTR_SERIAL_NOTIFY, + SessionID: id, + Len: RTR_SERIAL_NOTIFY_LEN, + SerialNumber: sn, + }, + } +} + +type RTRSerialQuery struct { + RTRCommon +} + +func NewRTRSerialQuery(id uint16, sn uint32) *RTRSerialQuery { + return &RTRSerialQuery{ + RTRCommon{ + Type: RTR_SERIAL_QUERY, + SessionID: id, + Len: RTR_SERIAL_QUERY_LEN, + SerialNumber: sn, + }, + } +} + +type RTRReset struct { + Version uint8 + Type uint8 + Len uint32 +} + +func (m *RTRReset) DecodeFromBytes(data []byte) error { + m.Version = data[0] + m.Type = data[1] + m.Len = binary.BigEndian.Uint32(data[4:8]) + return nil +} + +func (m *RTRReset) Serialize() ([]byte, error) { + data := make([]byte, m.Len) + data[0] = m.Version + data[1] = m.Type + binary.BigEndian.PutUint32(data[4:8], m.Len) + return data, nil +} + +type RTRResetQuery struct { + RTRReset +} + +func NewRTRResetQuery() *RTRResetQuery { + return &RTRResetQuery{ + RTRReset{ + Type: RTR_RESET_QUERY, + Len: RTR_RESET_QUERY_LEN, + }, + } +} + +type RTRCacheResponse struct { + Version uint8 + Type uint8 + SessionID uint16 + Len uint32 +} + +func (m *RTRCacheResponse) DecodeFromBytes(data []byte) error { + m.Version = data[0] + m.Type = data[1] + m.SessionID = binary.BigEndian.Uint16(data[2:4]) + m.Len = binary.BigEndian.Uint32(data[4:8]) + return nil +} + +func (m *RTRCacheResponse) Serialize() ([]byte, error) { + data := make([]byte, m.Len) + data[0] = m.Version + data[1] = m.Type + binary.BigEndian.PutUint16(data[2:4], m.SessionID) + binary.BigEndian.PutUint32(data[4:8], m.Len) + return data, nil +} + +func NewRTRCacheResponse(id uint16) *RTRCacheResponse { + return &RTRCacheResponse{ + Type: RTR_CACHE_RESPONSE, + SessionID: id, + Len: RTR_CACHE_RESPONSE_LEN, + } +} + +type RTRIPPrefix struct { + Version uint8 + Type uint8 + Len uint32 + Flags uint8 + PrefixLen uint8 + MaxLen uint8 + Prefix net.IP + AS uint32 +} + +func (m *RTRIPPrefix) DecodeFromBytes(data []byte) error { + m.Version = data[0] + m.Type = data[1] + m.Len = binary.BigEndian.Uint32(data[4:8]) + m.Flags = data[8] + m.PrefixLen = data[9] + m.MaxLen = data[10] + if m.Type == RTR_IPV4_PREFIX { + m.Prefix = net.IP(data[12:16]).To4() + m.AS = binary.BigEndian.Uint32(data[16:20]) + } else { + m.Prefix = net.IP(data[12:28]).To16() + m.AS = binary.BigEndian.Uint32(data[28:32]) + } + return nil +} + +func (m *RTRIPPrefix) Serialize() ([]byte, error) { + data := make([]byte, m.Len) + data[0] = m.Version + data[1] = m.Type + binary.BigEndian.PutUint32(data[4:8], m.Len) + data[8] = m.Flags + data[9] = m.PrefixLen + data[10] = m.MaxLen + if m.Type == RTR_IPV4_PREFIX { + copy(data[12:16], m.Prefix.To4()) + binary.BigEndian.PutUint32(data[16:20], m.AS) + } else { + copy(data[12:28], m.Prefix.To16()) + binary.BigEndian.PutUint32(data[28:32], m.AS) + } + return data, nil +} + +func NewRTRIPPrefix(prefix net.IP, prefixLen, maxLen uint8, as uint32, flags uint8) *RTRIPPrefix { + var pduType uint8 + var pduLen uint32 + if prefix.To4() != nil && prefixLen <= 32 { + pduType = RTR_IPV4_PREFIX + pduLen = RTR_IPV4_PREFIX_LEN + } else { + pduType = RTR_IPV6_PREFIX + pduLen = RTR_IPV6_PREFIX_LEN + } + + return &RTRIPPrefix{ + Type: pduType, + Len: pduLen, + Flags: flags, + PrefixLen: prefixLen, + MaxLen: maxLen, + Prefix: prefix, + AS: as, + } +} + +type RTREndOfData struct { + RTRCommon +} + +func NewRTREndOfData(id uint16, sn uint32) *RTREndOfData { + return &RTREndOfData{ + RTRCommon{ + Type: RTR_END_OF_DATA, + SessionID: id, + Len: RTR_END_OF_DATA_LEN, + SerialNumber: sn, + }, + } +} + +type RTRCacheReset struct { + RTRReset +} + +func NewRTRCacheReset() *RTRCacheReset { + return &RTRCacheReset{ + RTRReset{ + Type: RTR_CACHE_RESET, + Len: RTR_CACHE_RESET_LEN, + }, + } +} + +type RTRErrorReport struct { + Version uint8 + Type uint8 + ErrorCode uint16 + Len uint32 + PDULen uint32 + PDU []byte + TextLen uint32 + Text []byte +} + +func (m *RTRErrorReport) DecodeFromBytes(data []byte) error { + m.Version = data[0] + m.Type = data[1] + m.ErrorCode = binary.BigEndian.Uint16(data[2:4]) + m.Len = binary.BigEndian.Uint32(data[4:8]) + m.PDULen = binary.BigEndian.Uint32(data[8:12]) + m.PDU = make([]byte, m.PDULen) + copy(m.PDU, data[12:12+m.PDULen]) + m.TextLen = binary.BigEndian.Uint32(data[12+m.PDULen : 16+m.PDULen]) + m.Text = make([]byte, m.TextLen) + copy(m.Text, data[16+m.PDULen:]) + return nil +} + +func (m *RTRErrorReport) Serialize() ([]byte, error) { + data := make([]byte, m.Len) + data[0] = m.Version + data[1] = m.Type + binary.BigEndian.PutUint16(data[2:4], m.ErrorCode) + binary.BigEndian.PutUint32(data[4:8], m.Len) + binary.BigEndian.PutUint32(data[8:12], m.PDULen) + copy(data[12:], m.PDU) + binary.BigEndian.PutUint32(data[12+m.PDULen:16+m.PDULen], m.TextLen) + copy(data[16+m.PDULen:], m.Text) + return data, nil +} + +func NewRTRErrorReport(errCode uint16, errPDU []byte, errMsg []byte) *RTRErrorReport { + pdu := &RTRErrorReport{Type: RTR_ERROR_REPORT, ErrorCode: errCode} + if errPDU != nil { + if errPDU[1] == RTR_ERROR_REPORT { + return nil + } + pdu.PDULen = uint32(len(errPDU)) + pdu.PDU = errPDU + } + if errMsg != nil { + pdu.Text = errMsg + pdu.TextLen = uint32(len(errMsg)) + } + pdu.Len = uint32(RTR_MIN_LEN) + uint32(RTR_ERROR_REPORT_ERR_PDU_LEN) + pdu.PDULen + uint32(RTR_ERROR_REPORT_ERR_TEXT_LEN) + pdu.TextLen + return pdu +} + +func SplitRTR(data []byte, atEOF bool) (advance int, token []byte, err error) { + if atEOF && len(data) == 0 || len(data) < RTR_MIN_LEN { + return 0, nil, nil + } + + totalLen := binary.BigEndian.Uint32(data[4:8]) + if totalLen < RTR_MIN_LEN { + return 0, nil, fmt.Errorf("Invalid length: %d", totalLen) + } + if uint32(len(data)) < totalLen { + return 0, nil, nil + } + return int(totalLen), data[0:totalLen], nil +} + +func ParseRTR(data []byte) (RTRMessage, error) { + var msg RTRMessage + switch data[1] { + case RTR_SERIAL_NOTIFY: + msg = &RTRSerialNotify{} + case RTR_SERIAL_QUERY: + msg = &RTRSerialQuery{} + case RTR_RESET_QUERY: + msg = &RTRResetQuery{} + case RTR_CACHE_RESPONSE: + msg = &RTRCacheResponse{} + case RTR_IPV4_PREFIX: + msg = &RTRIPPrefix{} + case RTR_IPV6_PREFIX: + msg = &RTRIPPrefix{} + case RTR_END_OF_DATA: + msg = &RTREndOfData{} + case RTR_CACHE_RESET: + msg = &RTRCacheReset{} + case RTR_ERROR_REPORT: + msg = &RTRErrorReport{} + default: + return nil, fmt.Errorf("unknown RTR message type %d:", data[1]) + } + err := msg.DecodeFromBytes(data) + return msg, err +} |