From 6d401bedf5a947444a66daf5312719f6dba8b381 Mon Sep 17 00:00:00 2001 From: FUJITA Tomonori Date: Mon, 11 Apr 2016 09:09:50 +0900 Subject: packet: create mrt package move mrt stuff from bgp to mrt package. Signed-off-by: FUJITA Tomonori --- packet/bgp/mrt.go | 798 ------------------------------------------------ packet/bgp/mrt_test.go | 220 -------------- packet/mrt/mrt.go | 799 +++++++++++++++++++++++++++++++++++++++++++++++++ packet/mrt/mrt_test.go | 221 ++++++++++++++ 4 files changed, 1020 insertions(+), 1018 deletions(-) delete mode 100644 packet/bgp/mrt.go delete mode 100644 packet/bgp/mrt_test.go create mode 100644 packet/mrt/mrt.go create mode 100644 packet/mrt/mrt_test.go (limited to 'packet') diff --git a/packet/bgp/mrt.go b/packet/bgp/mrt.go deleted file mode 100644 index 15dbc839..00000000 --- a/packet/bgp/mrt.go +++ /dev/null @@ -1,798 +0,0 @@ -// 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 ( - "bytes" - "encoding/binary" - "fmt" - "math" - "net" - "time" -) - -const ( - MRT_COMMON_HEADER_LEN = 12 -) - -type MRTType uint16 - -const ( - NULL MRTType = 0 // deprecated - START MRTType = 1 // deprecated - DIE MRTType = 2 // deprecated - I_AM_DEAD MRTType = 3 // deprecated - PEER_DOWN MRTType = 4 // deprecated - BGP MRTType = 5 // deprecated - RIP MRTType = 6 // deprecated - IDRP MRTType = 7 // deprecated - RIPNG MRTType = 8 // deprecated - BGP4PLUS MRTType = 9 // deprecated - BGP4PLUS01 MRTType = 10 // deprecated - OSPFv2 MRTType = 11 - TABLE_DUMP MRTType = 12 - TABLE_DUMPv2 MRTType = 13 - BGP4MP MRTType = 16 - BGP4MP_ET MRTType = 17 - ISIS MRTType = 32 - ISIS_ET MRTType = 33 - OSPFv3 MRTType = 48 - OSPFv3_ET MRTType = 49 -) - -type MRTSubTyper interface { - ToUint16() uint16 -} - -type MRTSubTypeTableDumpv2 uint16 - -const ( - PEER_INDEX_TABLE MRTSubTypeTableDumpv2 = 1 - RIB_IPV4_UNICAST MRTSubTypeTableDumpv2 = 2 - RIB_IPV4_MULTICAST MRTSubTypeTableDumpv2 = 3 - RIB_IPV6_UNICAST MRTSubTypeTableDumpv2 = 4 - RIB_IPV6_MULTICAST MRTSubTypeTableDumpv2 = 5 - RIB_GENERIC MRTSubTypeTableDumpv2 = 6 -) - -func (t MRTSubTypeTableDumpv2) ToUint16() uint16 { - return uint16(t) -} - -type MRTSubTypeBGP4MP uint16 - -const ( - STATE_CHANGE MRTSubTypeBGP4MP = 0 - MESSAGE MRTSubTypeBGP4MP = 1 - MESSAGE_AS4 MRTSubTypeBGP4MP = 4 - STATE_CHANGE_AS4 MRTSubTypeBGP4MP = 5 - MESSAGE_LOCAL MRTSubTypeBGP4MP = 6 - MESSAGE_AS4_LOCAL MRTSubTypeBGP4MP = 7 -) - -func (t MRTSubTypeBGP4MP) ToUint16() uint16 { - return uint16(t) -} - -type BGPState uint16 - -const ( - IDLE BGPState = 1 - CONNECT BGPState = 2 - ACTIVE BGPState = 3 - OPENSENT BGPState = 4 - OPENCONFIRM BGPState = 5 - ESTABLISHED BGPState = 6 -) - -func packValues(values []interface{}) ([]byte, error) { - b := new(bytes.Buffer) - for _, v := range values { - err := binary.Write(b, binary.BigEndian, v) - if err != nil { - return nil, err - } - } - return b.Bytes(), nil -} - -type MRTHeader struct { - Timestamp uint32 - Type MRTType - SubType uint16 - Len uint32 -} - -func (h *MRTHeader) DecodeFromBytes(data []byte) error { - if len(data) < MRT_COMMON_HEADER_LEN { - return fmt.Errorf("not all MRTHeader bytes are available. expected: %d, actual: %d", MRT_COMMON_HEADER_LEN, len(data)) - } - h.Timestamp = binary.BigEndian.Uint32(data[:4]) - h.Type = MRTType(binary.BigEndian.Uint16(data[4:6])) - h.SubType = binary.BigEndian.Uint16(data[6:8]) - h.Len = binary.BigEndian.Uint32(data[8:12]) - return nil -} - -func (h *MRTHeader) Serialize() ([]byte, error) { - return packValues([]interface{}{h.Timestamp, h.Type, h.SubType, h.Len}) -} - -func NewMRTHeader(timestamp uint32, t MRTType, subtype MRTSubTyper, l uint32) (*MRTHeader, error) { - return &MRTHeader{ - Timestamp: timestamp, - Type: t, - SubType: subtype.ToUint16(), - Len: l, - }, nil -} - -func (h *MRTHeader) GetTime() time.Time { - t := int64(h.Timestamp) - return time.Unix(t, 0) -} - -type MRTMessage struct { - Header MRTHeader - Body Body -} - -func (m *MRTMessage) Serialize() ([]byte, error) { - buf, err := m.Body.Serialize() - if err != nil { - return nil, err - } - m.Header.Len = uint32(len(buf)) - bbuf, err := m.Header.Serialize() - if err != nil { - return nil, err - } - return append(bbuf, buf...), nil -} - -func NewMRTMessage(timestamp uint32, t MRTType, subtype MRTSubTyper, body Body) (*MRTMessage, error) { - header, err := NewMRTHeader(timestamp, t, subtype, 0) - if err != nil { - return nil, err - } - return &MRTMessage{ - Header: *header, - Body: body, - }, nil -} - -type Body interface { - DecodeFromBytes([]byte) error - Serialize() ([]byte, error) -} - -type Peer struct { - Type uint8 - BgpId net.IP - IpAddress net.IP - AS uint32 -} - -func (p *Peer) DecodeFromBytes(data []byte) ([]byte, error) { - notAllBytesAvail := fmt.Errorf("not all Peer bytes are available") - if len(data) < 5 { - return nil, notAllBytesAvail - } - p.Type = uint8(data[0]) - p.BgpId = net.IP(data[1:5]) - data = data[5:] - - if p.Type&1 > 0 { - if len(data) < 16 { - return nil, notAllBytesAvail - } - p.IpAddress = net.IP(data[:16]) - data = data[16:] - } else { - if len(data) < 4 { - return nil, notAllBytesAvail - } - p.IpAddress = net.IP(data[:4]) - data = data[4:] - } - - if p.Type&(1<<1) > 0 { - if len(data) < 4 { - return nil, notAllBytesAvail - } - p.AS = binary.BigEndian.Uint32(data[:4]) - data = data[4:] - } else { - if len(data) < 2 { - return nil, notAllBytesAvail - } - p.AS = uint32(binary.BigEndian.Uint16(data[:2])) - data = data[2:] - } - - return data, nil -} - -func (p *Peer) Serialize() ([]byte, error) { - var err error - var bbuf []byte - buf := make([]byte, 5) - buf[0] = uint8(p.Type) - copy(buf[1:], p.BgpId.To4()) - if p.Type&1 > 0 { - buf = append(buf, p.IpAddress.To16()...) - } else { - buf = append(buf, p.IpAddress.To4()...) - } - if p.Type&(1<<1) > 0 { - bbuf, err = packValues([]interface{}{p.AS}) - } else { - if p.AS > uint32(math.MaxUint16) { - return nil, fmt.Errorf("AS number is beyond 2 octet. %d > %d", p.AS, math.MaxUint16) - } - bbuf, err = packValues([]interface{}{uint16(p.AS)}) - } - if err != nil { - return nil, err - } - return append(buf, bbuf...), nil -} - -func NewPeer(bgpid string, ipaddr string, asn uint32, isAS4 bool) *Peer { - t := 0 - addr := net.ParseIP(ipaddr).To4() - if addr == nil { - t |= 1 - addr = net.ParseIP(ipaddr).To16() - } - if isAS4 { - t |= (1 << 1) - } - return &Peer{ - Type: uint8(t), - BgpId: net.ParseIP(bgpid).To4(), - IpAddress: addr, - AS: asn, - } -} - -func (p *Peer) String() string { - return fmt.Sprintf("PEER ENTRY: ID [%s] Addr [%s] AS [%d]", p.BgpId, p.IpAddress, p.AS) -} - -type PeerIndexTable struct { - CollectorBgpId net.IP - ViewName string - Peers []*Peer -} - -func (t *PeerIndexTable) DecodeFromBytes(data []byte) error { - notAllBytesAvail := fmt.Errorf("not all PeerIndexTable bytes are available") - if len(data) < 6 { - return notAllBytesAvail - } - t.CollectorBgpId = net.IP(data[:4]) - viewLen := binary.BigEndian.Uint16(data[4:6]) - if len(data) < 6+int(viewLen) { - return notAllBytesAvail - } - t.ViewName = string(data[6 : 6+viewLen]) - - data = data[6+viewLen:] - - if len(data) < 2 { - return notAllBytesAvail - } - peerNum := binary.BigEndian.Uint16(data[:2]) - data = data[2:] - t.Peers = make([]*Peer, 0, peerNum) - var err error - for i := 0; i < int(peerNum); i++ { - p := &Peer{} - data, err = p.DecodeFromBytes(data) - if err != nil { - return err - } - t.Peers = append(t.Peers, p) - } - - return nil -} - -func (t *PeerIndexTable) Serialize() ([]byte, error) { - buf := make([]byte, 8+len(t.ViewName)) - copy(buf, t.CollectorBgpId.To4()) - binary.BigEndian.PutUint16(buf[4:], uint16(len(t.ViewName))) - copy(buf[6:], t.ViewName) - binary.BigEndian.PutUint16(buf[6+len(t.ViewName):], uint16(len(t.Peers))) - for _, peer := range t.Peers { - bbuf, err := peer.Serialize() - if err != nil { - return nil, err - } - buf = append(buf, bbuf...) - } - return buf, nil -} - -func NewPeerIndexTable(bgpid string, viewname string, peers []*Peer) *PeerIndexTable { - return &PeerIndexTable{ - CollectorBgpId: net.ParseIP(bgpid).To4(), - ViewName: viewname, - Peers: peers, - } -} - -func (t *PeerIndexTable) String() string { - return fmt.Sprintf("PEER_INDEX_TABLE: CollectorBgpId [%s] ViewName [%s] Peers [%s]", t.CollectorBgpId, t.ViewName, t.Peers) -} - -type RibEntry struct { - PeerIndex uint16 - OriginatedTime uint32 - PathAttributes []PathAttributeInterface -} - -func (e *RibEntry) DecodeFromBytes(data []byte) ([]byte, error) { - notAllBytesAvail := fmt.Errorf("not all RibEntry bytes are available") - if len(data) < 8 { - return nil, notAllBytesAvail - } - e.PeerIndex = binary.BigEndian.Uint16(data[:2]) - e.OriginatedTime = binary.BigEndian.Uint32(data[2:6]) - totalLen := binary.BigEndian.Uint16(data[6:8]) - data = data[8:] - for attrLen := totalLen; attrLen > 0; { - p, err := GetPathAttribute(data) - if err != nil { - return nil, err - } - err = p.DecodeFromBytes(data) - if err != nil { - return nil, err - } - attrLen -= uint16(p.Len()) - if len(data) < p.Len() { - return nil, notAllBytesAvail - } - data = data[p.Len():] - e.PathAttributes = append(e.PathAttributes, p) - } - return data, nil -} - -func (e *RibEntry) Serialize() ([]byte, error) { - buf := make([]byte, 8) - binary.BigEndian.PutUint16(buf, e.PeerIndex) - binary.BigEndian.PutUint32(buf[2:], e.OriginatedTime) - totalLen := 0 - binary.BigEndian.PutUint16(buf[6:], uint16(totalLen)) - for _, pattr := range e.PathAttributes { - // TODO special modification is needed for MP_REACH_NLRI - // but also Quagga doesn't implement this. - // - // RFC 6396 4.3.4 - // There is one exception to the encoding of BGP attributes for the BGP - // MP_REACH_NLRI attribute (BGP Type Code 14). - // Since the AFI, SAFI, and NLRI information is already encoded - // in the RIB Entry Header or RIB_GENERIC Entry Header, - // only the Next Hop Address Length and Next Hop Address fields are included. - - bbuf, err := pattr.Serialize() - if err != nil { - return nil, err - } - buf = append(buf, bbuf...) - totalLen += len(bbuf) - } - binary.BigEndian.PutUint16(buf[6:], uint16(totalLen)) - return buf, nil -} - -func NewRibEntry(index uint16, time uint32, pathattrs []PathAttributeInterface) *RibEntry { - return &RibEntry{ - PeerIndex: index, - OriginatedTime: time, - PathAttributes: pathattrs, - } -} - -func (e *RibEntry) String() string { - return fmt.Sprintf("RIB_ENTRY: PeerIndex [%d] OriginatedTime [%d] PathAttrs [%v]", e.PeerIndex, e.OriginatedTime, e.PathAttributes) -} - -type Rib struct { - SequenceNumber uint32 - Prefix AddrPrefixInterface - Entries []*RibEntry - RouteFamily RouteFamily -} - -func (u *Rib) DecodeFromBytes(data []byte) error { - if len(data) < 4 { - return fmt.Errorf("Not all RibIpv4Unicast message bytes available") - } - u.SequenceNumber = binary.BigEndian.Uint32(data[:4]) - data = data[4:] - afi, safi := RouteFamilyToAfiSafi(u.RouteFamily) - if afi == 0 && safi == 0 { - afi = binary.BigEndian.Uint16(data[:2]) - safi = data[2] - data = data[3:] - } - prefix, err := NewPrefixFromRouteFamily(afi, safi) - if err != nil { - return err - } - err = prefix.DecodeFromBytes(data) - if err != nil { - return err - } - u.Prefix = prefix - data = data[prefix.Len():] - entryNum := binary.BigEndian.Uint16(data[:2]) - data = data[2:] - u.Entries = make([]*RibEntry, 0, entryNum) - for i := 0; i < int(entryNum); i++ { - e := &RibEntry{} - data, err = e.DecodeFromBytes(data) - if err != nil { - return err - } - u.Entries = append(u.Entries, e) - } - return nil -} - -func (u *Rib) Serialize() ([]byte, error) { - buf := make([]byte, 4) - binary.BigEndian.PutUint32(buf, u.SequenceNumber) - rf := AfiSafiToRouteFamily(u.Prefix.AFI(), u.Prefix.SAFI()) - switch rf { - case RF_IPv4_UC, RF_IPv4_MC, RF_IPv6_UC, RF_IPv6_MC: - default: - bbuf := make([]byte, 0, 2) - binary.BigEndian.PutUint16(bbuf, u.Prefix.AFI()) - buf = append(buf, bbuf...) - buf = append(buf, u.Prefix.SAFI()) - } - bbuf, err := u.Prefix.Serialize() - if err != nil { - return nil, err - } - buf = append(buf, bbuf...) - bbuf, err = packValues([]interface{}{uint16(len(u.Entries))}) - if err != nil { - return nil, err - } - buf = append(buf, bbuf...) - for _, entry := range u.Entries { - bbuf, err = entry.Serialize() - if err != nil { - return nil, err - } - buf = append(buf, bbuf...) - } - return buf, nil -} - -func NewRib(seq uint32, prefix AddrPrefixInterface, entries []*RibEntry) *Rib { - rf := AfiSafiToRouteFamily(prefix.AFI(), prefix.SAFI()) - return &Rib{ - SequenceNumber: seq, - Prefix: prefix, - Entries: entries, - RouteFamily: rf, - } -} - -func (u *Rib) String() string { - return fmt.Sprintf("RIB: Seq [%d] Prefix [%s] Entries [%s]", u.SequenceNumber, u.Prefix, u.Entries) -} - -type BGP4MPHeader struct { - PeerAS uint32 - LocalAS uint32 - InterfaceIndex uint16 - AddressFamily uint16 - PeerIpAddress net.IP - LocalIpAddress net.IP - isAS4 bool -} - -func (m *BGP4MPHeader) decodeFromBytes(data []byte) ([]byte, error) { - if m.isAS4 && len(data) < 8 { - return nil, fmt.Errorf("Not all BGP4MPMessageAS4 bytes available") - } else if !m.isAS4 && len(data) < 4 { - return nil, fmt.Errorf("Not all BGP4MPMessageAS bytes available") - } - - if m.isAS4 { - m.PeerAS = binary.BigEndian.Uint32(data[:4]) - m.LocalAS = binary.BigEndian.Uint32(data[4:8]) - data = data[8:] - } else { - m.PeerAS = uint32(binary.BigEndian.Uint16(data[:2])) - m.LocalAS = uint32(binary.BigEndian.Uint16(data[2:4])) - data = data[4:] - } - m.InterfaceIndex = binary.BigEndian.Uint16(data[:2]) - m.AddressFamily = binary.BigEndian.Uint16(data[2:4]) - switch m.AddressFamily { - case AFI_IP: - m.PeerIpAddress = net.IP(data[4:8]).To4() - m.LocalIpAddress = net.IP(data[8:12]).To4() - data = data[12:] - case AFI_IP6: - m.PeerIpAddress = net.IP(data[4:20]) - m.LocalIpAddress = net.IP(data[20:36]) - data = data[36:] - default: - return nil, fmt.Errorf("unsupported address family: %d", m.AddressFamily) - } - return data, nil -} - -func (m *BGP4MPHeader) serialize() ([]byte, error) { - var values []interface{} - if m.isAS4 { - values = []interface{}{m.PeerAS, m.LocalAS, m.InterfaceIndex, m.AddressFamily} - } else { - values = []interface{}{uint16(m.PeerAS), uint16(m.LocalAS), m.InterfaceIndex, m.AddressFamily} - } - buf, err := packValues(values) - if err != nil { - return nil, err - } - var bbuf []byte - switch m.AddressFamily { - case AFI_IP: - bbuf = make([]byte, 8) - copy(bbuf, m.PeerIpAddress.To4()) - copy(bbuf[4:], m.LocalIpAddress.To4()) - case AFI_IP6: - bbuf = make([]byte, 32) - copy(bbuf, m.PeerIpAddress) - copy(bbuf[16:], m.LocalIpAddress) - default: - return nil, fmt.Errorf("unsupported address family: %d", m.AddressFamily) - } - return append(buf, bbuf...), nil -} - -func newBGP4MPHeader(peeras, localas uint32, intfindex uint16, peerip, localip string, isAS4 bool) (*BGP4MPHeader, error) { - var af uint16 - paddr := net.ParseIP(peerip).To4() - laddr := net.ParseIP(localip).To4() - if paddr != nil && laddr != nil { - af = AFI_IP - } else { - paddr = net.ParseIP(peerip).To16() - laddr = net.ParseIP(localip).To16() - if paddr != nil && laddr != nil { - af = AFI_IP6 - } else { - return nil, fmt.Errorf("Peer IP Address and Local IP Address must have the same address family") - } - } - return &BGP4MPHeader{ - PeerAS: peeras, - LocalAS: localas, - InterfaceIndex: intfindex, - AddressFamily: af, - PeerIpAddress: paddr, - LocalIpAddress: laddr, - isAS4: isAS4, - }, nil -} - -type BGP4MPStateChange struct { - *BGP4MPHeader - OldState BGPState - NewState BGPState -} - -func (m *BGP4MPStateChange) DecodeFromBytes(data []byte) error { - rest, err := m.decodeFromBytes(data) - if err != nil { - return err - } - if len(rest) < 4 { - return fmt.Errorf("Not all BGP4MPStateChange bytes available") - } - m.OldState = BGPState(binary.BigEndian.Uint16(rest[:2])) - m.NewState = BGPState(binary.BigEndian.Uint16(rest[2:4])) - return nil -} - -func (m *BGP4MPStateChange) Serialize() ([]byte, error) { - buf, err := m.serialize() - if err != nil { - return nil, err - } - bbuf, err := packValues([]interface{}{m.OldState, m.NewState}) - if err != nil { - return nil, err - } - return append(buf, bbuf...), nil -} - -func NewBGP4MPStateChange(peeras, localas uint32, intfindex uint16, peerip, localip string, isAS4 bool, oldstate, newstate BGPState) *BGP4MPStateChange { - header, _ := newBGP4MPHeader(peeras, localas, intfindex, peerip, localip, isAS4) - return &BGP4MPStateChange{ - BGP4MPHeader: header, - OldState: oldstate, - NewState: newstate, - } -} - -type BGP4MPMessage struct { - *BGP4MPHeader - BGPMessage *BGPMessage - BGPMessagePayload []byte - isLocal bool -} - -func (m *BGP4MPMessage) DecodeFromBytes(data []byte) error { - rest, err := m.decodeFromBytes(data) - if err != nil { - return err - } - - if len(rest) < BGP_HEADER_LENGTH { - return fmt.Errorf("Not all BGP4MPMessageAS4 bytes available") - } - - msg, err := ParseBGPMessage(rest) - if err != nil { - return err - } - m.BGPMessage = msg - return nil -} - -func (m *BGP4MPMessage) Serialize() ([]byte, error) { - buf, err := m.serialize() - if err != nil { - return nil, err - } - if m.BGPMessagePayload != nil { - return append(buf, m.BGPMessagePayload...), nil - } - bbuf, err := m.BGPMessage.Serialize() - if err != nil { - return nil, err - } - return append(buf, bbuf...), nil -} - -func NewBGP4MPMessage(peeras, localas uint32, intfindex uint16, peerip, localip string, isAS4 bool, msg *BGPMessage) *BGP4MPMessage { - header, _ := newBGP4MPHeader(peeras, localas, intfindex, peerip, localip, isAS4) - return &BGP4MPMessage{ - BGP4MPHeader: header, - BGPMessage: msg, - } -} - -func NewBGP4MPMessageLocal(peeras, localas uint32, intfindex uint16, peerip, localip string, isAS4 bool, msg *BGPMessage) *BGP4MPMessage { - header, _ := newBGP4MPHeader(peeras, localas, intfindex, peerip, localip, isAS4) - return &BGP4MPMessage{ - BGP4MPHeader: header, - BGPMessage: msg, - isLocal: true, - } -} - -func (m *BGP4MPMessage) String() string { - title := "BGP4MP_MSG" - if m.isAS4 { - title += "_AS4" - } - if m.isLocal { - title += "_LOCAL" - } - return fmt.Sprintf("%s: PeerAS [%d] LocalAS [%d] InterfaceIndex [%d] PeerIP [%s] LocalIP [%s] BGPMessage [%v]", title, m.PeerAS, m.LocalAS, m.InterfaceIndex, m.PeerIpAddress, m.LocalIpAddress, m.BGPMessage) -} - -//This function can be passed into a bufio.Scanner.Split() to read buffered mrt msgs -func SplitMrt(data []byte, atEOF bool) (advance int, token []byte, err error) { - if atEOF && len(data) == 0 { - return 0, nil, nil - } - if cap(data) < MRT_COMMON_HEADER_LEN { // read more - return 0, nil, nil - } - //this reads the data - hdr := &MRTHeader{} - errh := hdr.DecodeFromBytes(data[:MRT_COMMON_HEADER_LEN]) - if errh != nil { - return 0, nil, errh - } - totlen := int(hdr.Len + MRT_COMMON_HEADER_LEN) - if len(data) < totlen { //need to read more - return 0, nil, nil - } - return totlen, data[0:totlen], nil -} - -func ParseMRTBody(h *MRTHeader, data []byte) (*MRTMessage, error) { - if len(data) < int(h.Len) { - return nil, fmt.Errorf("Not all MRT message bytes available. expected: %d, actual: %d", int(h.Len), len(data)) - } - msg := &MRTMessage{Header: *h} - switch h.Type { - case TABLE_DUMPv2: - subType := MRTSubTypeTableDumpv2(h.SubType) - rf := RouteFamily(0) - switch subType { - case PEER_INDEX_TABLE: - msg.Body = &PeerIndexTable{} - case RIB_IPV4_UNICAST: - rf = RF_IPv4_UC - case RIB_IPV4_MULTICAST: - rf = RF_IPv4_MC - case RIB_IPV6_UNICAST: - rf = RF_IPv6_UC - case RIB_IPV6_MULTICAST: - rf = RF_IPv6_MC - case RIB_GENERIC: - default: - return nil, fmt.Errorf("unsupported table dumpv2 subtype: %v\n", subType) - } - - if subType != PEER_INDEX_TABLE { - msg.Body = &Rib{ - RouteFamily: rf, - } - } - case BGP4MP: - subType := MRTSubTypeBGP4MP(h.SubType) - isAS4 := true - switch subType { - case STATE_CHANGE: - isAS4 = false - fallthrough - case STATE_CHANGE_AS4: - msg.Body = &BGP4MPStateChange{ - BGP4MPHeader: &BGP4MPHeader{isAS4: isAS4}, - } - case MESSAGE: - isAS4 = false - fallthrough - case MESSAGE_AS4: - msg.Body = &BGP4MPMessage{ - BGP4MPHeader: &BGP4MPHeader{isAS4: isAS4}, - } - case MESSAGE_LOCAL: - isAS4 = false - fallthrough - case MESSAGE_AS4_LOCAL: - msg.Body = &BGP4MPMessage{ - BGP4MPHeader: &BGP4MPHeader{isAS4: isAS4}, - isLocal: true, - } - default: - return nil, fmt.Errorf("unsupported bgp4mp subtype: %v\n", subType) - } - default: - return nil, fmt.Errorf("unsupported type: %v\n", h.Type) - } - err := msg.Body.DecodeFromBytes(data) - if err != nil { - return nil, err - } - return msg, nil -} diff --git a/packet/bgp/mrt_test.go b/packet/bgp/mrt_test.go deleted file mode 100644 index 11a233ec..00000000 --- a/packet/bgp/mrt_test.go +++ /dev/null @@ -1,220 +0,0 @@ -// 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 ( - "bufio" - "bytes" - "github.com/stretchr/testify/assert" - "reflect" - "testing" - "time" -) - -func TestMrtHdr(t *testing.T) { - h1, err := NewMRTHeader(10, TABLE_DUMPv2, RIB_IPV4_MULTICAST, 20) - if err != nil { - t.Fatal(err) - } - b1, err := h1.Serialize() - if err != nil { - t.Fatal(err) - } - h2 := &MRTHeader{} - err = h2.DecodeFromBytes(b1) - if err != nil { - t.Fatal(err) - } - assert.Equal(t, reflect.DeepEqual(h1, h2), true) -} - -func TestMrtHdrTime(t *testing.T) { - h1, err := NewMRTHeader(10, TABLE_DUMPv2, RIB_IPV4_MULTICAST, 20) - if err != nil { - t.Fatal(err) - } - ttime := time.Unix(10, 0) - htime := h1.GetTime() - t.Logf("this timestamp should be 10s after epoch:%v", htime) - assert.Equal(t, h1.GetTime(), ttime) -} - -func testPeer(t *testing.T, p1 *Peer) { - b1, err := p1.Serialize() - if err != nil { - t.Fatal(err) - } - p2 := &Peer{} - rest, err := p2.DecodeFromBytes(b1) - if err != nil { - t.Fatal(err) - } - assert.Equal(t, len(rest), 0) - assert.Equal(t, reflect.DeepEqual(p1, p2), true) -} - -func TestMrtPeer(t *testing.T) { - p := NewPeer("192.168.0.1", "10.0.0.1", 65000, false) - testPeer(t, p) -} - -func TestMrtPeerv6(t *testing.T) { - p := NewPeer("192.168.0.1", "2001::1", 65000, false) - testPeer(t, p) -} - -func TestMrtPeerAS4(t *testing.T) { - p := NewPeer("192.168.0.1", "2001::1", 135500, true) - testPeer(t, p) -} - -func TestMrtPeerIndexTable(t *testing.T) { - p1 := NewPeer("192.168.0.1", "10.0.0.1", 65000, false) - p2 := NewPeer("192.168.0.1", "2001::1", 65000, false) - p3 := NewPeer("192.168.0.1", "2001::1", 135500, true) - pt1 := NewPeerIndexTable("192.168.0.1", "test", []*Peer{p1, p2, p3}) - b1, err := pt1.Serialize() - if err != nil { - t.Fatal(err) - } - pt2 := &PeerIndexTable{} - err = pt2.DecodeFromBytes(b1) - if err != nil { - t.Fatal(err) - } - assert.Equal(t, reflect.DeepEqual(pt1, pt2), true) -} - -func TestMrtRibEntry(t *testing.T) { - aspath1 := []AsPathParamInterface{ - NewAsPathParam(2, []uint16{1000}), - NewAsPathParam(1, []uint16{1001, 1002}), - NewAsPathParam(2, []uint16{1003, 1004}), - } - - p := []PathAttributeInterface{ - NewPathAttributeOrigin(3), - NewPathAttributeAsPath(aspath1), - NewPathAttributeNextHop("129.1.1.2"), - NewPathAttributeMultiExitDisc(1 << 20), - NewPathAttributeLocalPref(1 << 22), - } - - e1 := NewRibEntry(1, uint32(time.Now().Unix()), p) - b1, err := e1.Serialize() - if err != nil { - t.Fatal(err) - } - - e2 := &RibEntry{} - rest, err := e2.DecodeFromBytes(b1) - if err != nil { - t.Fatal(err) - } - assert.Equal(t, len(rest), 0) - assert.Equal(t, reflect.DeepEqual(e1, e2), true) -} - -func TestMrtRib(t *testing.T) { - aspath1 := []AsPathParamInterface{ - NewAsPathParam(2, []uint16{1000}), - NewAsPathParam(1, []uint16{1001, 1002}), - NewAsPathParam(2, []uint16{1003, 1004}), - } - - p := []PathAttributeInterface{ - NewPathAttributeOrigin(3), - NewPathAttributeAsPath(aspath1), - NewPathAttributeNextHop("129.1.1.2"), - NewPathAttributeMultiExitDisc(1 << 20), - NewPathAttributeLocalPref(1 << 22), - } - - e1 := NewRibEntry(1, uint32(time.Now().Unix()), p) - e2 := NewRibEntry(2, uint32(time.Now().Unix()), p) - e3 := NewRibEntry(3, uint32(time.Now().Unix()), p) - - r1 := NewRib(1, NewIPAddrPrefix(24, "192.168.0.0"), []*RibEntry{e1, e2, e3}) - b1, err := r1.Serialize() - if err != nil { - t.Fatal(err) - } - r2 := &Rib{ - RouteFamily: RF_IPv4_UC, - } - err = r2.DecodeFromBytes(b1) - if err != nil { - t.Fatal(err) - } - assert.Equal(t, reflect.DeepEqual(r1, r2), true) -} - -func TestMrtBgp4mpStateChange(t *testing.T) { - c1 := NewBGP4MPStateChange(65000, 65001, 1, "192.168.0.1", "192.168.0.2", false, ACTIVE, ESTABLISHED) - b1, err := c1.Serialize() - if err != nil { - t.Fatal(err) - } - c2 := &BGP4MPStateChange{BGP4MPHeader: &BGP4MPHeader{}} - err = c2.DecodeFromBytes(b1) - if err != nil { - t.Fatal(err) - } - _, err = c2.Serialize() - if err != nil { - t.Fatal(err) - } - assert.Equal(t, reflect.DeepEqual(c1, c2), true) -} - -func TestMrtBgp4mpMessage(t *testing.T) { - msg := NewBGPKeepAliveMessage() - m1 := NewBGP4MPMessage(65000, 65001, 1, "192.168.0.1", "192.168.0.2", false, msg) - b1, err := m1.Serialize() - if err != nil { - t.Fatal(err) - } - m2 := &BGP4MPMessage{BGP4MPHeader: &BGP4MPHeader{}} - err = m2.DecodeFromBytes(b1) - if err != nil { - t.Fatal(err) - } - assert.Equal(t, reflect.DeepEqual(m1, m2), true) -} - -func TestMrtSplit(t *testing.T) { - var b bytes.Buffer - numwrite, numread := 10, 0 - for i := 0; i < numwrite; i++ { - msg := NewBGPKeepAliveMessage() - m1 := NewBGP4MPMessage(65000, 65001, 1, "192.168.0.1", "192.168.0.2", false, msg) - mm, _ := NewMRTMessage(1234, BGP4MP, MESSAGE, m1) - b1, err := mm.Serialize() - if err != nil { - t.Fatal(err) - } - b.Write(b1) - } - t.Logf("wrote %d serialized MRT keepalives in the buffer", numwrite) - r := bytes.NewReader(b.Bytes()) - scanner := bufio.NewScanner(r) - scanner.Split(SplitMrt) - for scanner.Scan() { - numread += 1 - } - t.Logf("scanner scanned %d serialized keepalives from the buffer", numread) - assert.Equal(t, numwrite, numread) -} diff --git a/packet/mrt/mrt.go b/packet/mrt/mrt.go new file mode 100644 index 00000000..041a2802 --- /dev/null +++ b/packet/mrt/mrt.go @@ -0,0 +1,799 @@ +// 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 mrt + +import ( + "bytes" + "encoding/binary" + "fmt" + "github.com/osrg/gobgp/packet/bgp" + "math" + "net" + "time" +) + +const ( + MRT_COMMON_HEADER_LEN = 12 +) + +type MRTType uint16 + +const ( + NULL MRTType = 0 // deprecated + START MRTType = 1 // deprecated + DIE MRTType = 2 // deprecated + I_AM_DEAD MRTType = 3 // deprecated + PEER_DOWN MRTType = 4 // deprecated + BGP MRTType = 5 // deprecated + RIP MRTType = 6 // deprecated + IDRP MRTType = 7 // deprecated + RIPNG MRTType = 8 // deprecated + BGP4PLUS MRTType = 9 // deprecated + BGP4PLUS01 MRTType = 10 // deprecated + OSPFv2 MRTType = 11 + TABLE_DUMP MRTType = 12 + TABLE_DUMPv2 MRTType = 13 + BGP4MP MRTType = 16 + BGP4MP_ET MRTType = 17 + ISIS MRTType = 32 + ISIS_ET MRTType = 33 + OSPFv3 MRTType = 48 + OSPFv3_ET MRTType = 49 +) + +type MRTSubTyper interface { + ToUint16() uint16 +} + +type MRTSubTypeTableDumpv2 uint16 + +const ( + PEER_INDEX_TABLE MRTSubTypeTableDumpv2 = 1 + RIB_IPV4_UNICAST MRTSubTypeTableDumpv2 = 2 + RIB_IPV4_MULTICAST MRTSubTypeTableDumpv2 = 3 + RIB_IPV6_UNICAST MRTSubTypeTableDumpv2 = 4 + RIB_IPV6_MULTICAST MRTSubTypeTableDumpv2 = 5 + RIB_GENERIC MRTSubTypeTableDumpv2 = 6 +) + +func (t MRTSubTypeTableDumpv2) ToUint16() uint16 { + return uint16(t) +} + +type MRTSubTypeBGP4MP uint16 + +const ( + STATE_CHANGE MRTSubTypeBGP4MP = 0 + MESSAGE MRTSubTypeBGP4MP = 1 + MESSAGE_AS4 MRTSubTypeBGP4MP = 4 + STATE_CHANGE_AS4 MRTSubTypeBGP4MP = 5 + MESSAGE_LOCAL MRTSubTypeBGP4MP = 6 + MESSAGE_AS4_LOCAL MRTSubTypeBGP4MP = 7 +) + +func (t MRTSubTypeBGP4MP) ToUint16() uint16 { + return uint16(t) +} + +type BGPState uint16 + +const ( + IDLE BGPState = 1 + CONNECT BGPState = 2 + ACTIVE BGPState = 3 + OPENSENT BGPState = 4 + OPENCONFIRM BGPState = 5 + ESTABLISHED BGPState = 6 +) + +func packValues(values []interface{}) ([]byte, error) { + b := new(bytes.Buffer) + for _, v := range values { + err := binary.Write(b, binary.BigEndian, v) + if err != nil { + return nil, err + } + } + return b.Bytes(), nil +} + +type MRTHeader struct { + Timestamp uint32 + Type MRTType + SubType uint16 + Len uint32 +} + +func (h *MRTHeader) DecodeFromBytes(data []byte) error { + if len(data) < MRT_COMMON_HEADER_LEN { + return fmt.Errorf("not all MRTHeader bytes are available. expected: %d, actual: %d", MRT_COMMON_HEADER_LEN, len(data)) + } + h.Timestamp = binary.BigEndian.Uint32(data[:4]) + h.Type = MRTType(binary.BigEndian.Uint16(data[4:6])) + h.SubType = binary.BigEndian.Uint16(data[6:8]) + h.Len = binary.BigEndian.Uint32(data[8:12]) + return nil +} + +func (h *MRTHeader) Serialize() ([]byte, error) { + return packValues([]interface{}{h.Timestamp, h.Type, h.SubType, h.Len}) +} + +func NewMRTHeader(timestamp uint32, t MRTType, subtype MRTSubTyper, l uint32) (*MRTHeader, error) { + return &MRTHeader{ + Timestamp: timestamp, + Type: t, + SubType: subtype.ToUint16(), + Len: l, + }, nil +} + +func (h *MRTHeader) GetTime() time.Time { + t := int64(h.Timestamp) + return time.Unix(t, 0) +} + +type MRTMessage struct { + Header MRTHeader + Body Body +} + +func (m *MRTMessage) Serialize() ([]byte, error) { + buf, err := m.Body.Serialize() + if err != nil { + return nil, err + } + m.Header.Len = uint32(len(buf)) + bbuf, err := m.Header.Serialize() + if err != nil { + return nil, err + } + return append(bbuf, buf...), nil +} + +func NewMRTMessage(timestamp uint32, t MRTType, subtype MRTSubTyper, body Body) (*MRTMessage, error) { + header, err := NewMRTHeader(timestamp, t, subtype, 0) + if err != nil { + return nil, err + } + return &MRTMessage{ + Header: *header, + Body: body, + }, nil +} + +type Body interface { + DecodeFromBytes([]byte) error + Serialize() ([]byte, error) +} + +type Peer struct { + Type uint8 + BgpId net.IP + IpAddress net.IP + AS uint32 +} + +func (p *Peer) DecodeFromBytes(data []byte) ([]byte, error) { + notAllBytesAvail := fmt.Errorf("not all Peer bytes are available") + if len(data) < 5 { + return nil, notAllBytesAvail + } + p.Type = uint8(data[0]) + p.BgpId = net.IP(data[1:5]) + data = data[5:] + + if p.Type&1 > 0 { + if len(data) < 16 { + return nil, notAllBytesAvail + } + p.IpAddress = net.IP(data[:16]) + data = data[16:] + } else { + if len(data) < 4 { + return nil, notAllBytesAvail + } + p.IpAddress = net.IP(data[:4]) + data = data[4:] + } + + if p.Type&(1<<1) > 0 { + if len(data) < 4 { + return nil, notAllBytesAvail + } + p.AS = binary.BigEndian.Uint32(data[:4]) + data = data[4:] + } else { + if len(data) < 2 { + return nil, notAllBytesAvail + } + p.AS = uint32(binary.BigEndian.Uint16(data[:2])) + data = data[2:] + } + + return data, nil +} + +func (p *Peer) Serialize() ([]byte, error) { + var err error + var bbuf []byte + buf := make([]byte, 5) + buf[0] = uint8(p.Type) + copy(buf[1:], p.BgpId.To4()) + if p.Type&1 > 0 { + buf = append(buf, p.IpAddress.To16()...) + } else { + buf = append(buf, p.IpAddress.To4()...) + } + if p.Type&(1<<1) > 0 { + bbuf, err = packValues([]interface{}{p.AS}) + } else { + if p.AS > uint32(math.MaxUint16) { + return nil, fmt.Errorf("AS number is beyond 2 octet. %d > %d", p.AS, math.MaxUint16) + } + bbuf, err = packValues([]interface{}{uint16(p.AS)}) + } + if err != nil { + return nil, err + } + return append(buf, bbuf...), nil +} + +func NewPeer(bgpid string, ipaddr string, asn uint32, isAS4 bool) *Peer { + t := 0 + addr := net.ParseIP(ipaddr).To4() + if addr == nil { + t |= 1 + addr = net.ParseIP(ipaddr).To16() + } + if isAS4 { + t |= (1 << 1) + } + return &Peer{ + Type: uint8(t), + BgpId: net.ParseIP(bgpid).To4(), + IpAddress: addr, + AS: asn, + } +} + +func (p *Peer) String() string { + return fmt.Sprintf("PEER ENTRY: ID [%s] Addr [%s] AS [%d]", p.BgpId, p.IpAddress, p.AS) +} + +type PeerIndexTable struct { + CollectorBgpId net.IP + ViewName string + Peers []*Peer +} + +func (t *PeerIndexTable) DecodeFromBytes(data []byte) error { + notAllBytesAvail := fmt.Errorf("not all PeerIndexTable bytes are available") + if len(data) < 6 { + return notAllBytesAvail + } + t.CollectorBgpId = net.IP(data[:4]) + viewLen := binary.BigEndian.Uint16(data[4:6]) + if len(data) < 6+int(viewLen) { + return notAllBytesAvail + } + t.ViewName = string(data[6 : 6+viewLen]) + + data = data[6+viewLen:] + + if len(data) < 2 { + return notAllBytesAvail + } + peerNum := binary.BigEndian.Uint16(data[:2]) + data = data[2:] + t.Peers = make([]*Peer, 0, peerNum) + var err error + for i := 0; i < int(peerNum); i++ { + p := &Peer{} + data, err = p.DecodeFromBytes(data) + if err != nil { + return err + } + t.Peers = append(t.Peers, p) + } + + return nil +} + +func (t *PeerIndexTable) Serialize() ([]byte, error) { + buf := make([]byte, 8+len(t.ViewName)) + copy(buf, t.CollectorBgpId.To4()) + binary.BigEndian.PutUint16(buf[4:], uint16(len(t.ViewName))) + copy(buf[6:], t.ViewName) + binary.BigEndian.PutUint16(buf[6+len(t.ViewName):], uint16(len(t.Peers))) + for _, peer := range t.Peers { + bbuf, err := peer.Serialize() + if err != nil { + return nil, err + } + buf = append(buf, bbuf...) + } + return buf, nil +} + +func NewPeerIndexTable(bgpid string, viewname string, peers []*Peer) *PeerIndexTable { + return &PeerIndexTable{ + CollectorBgpId: net.ParseIP(bgpid).To4(), + ViewName: viewname, + Peers: peers, + } +} + +func (t *PeerIndexTable) String() string { + return fmt.Sprintf("PEER_INDEX_TABLE: CollectorBgpId [%s] ViewName [%s] Peers [%s]", t.CollectorBgpId, t.ViewName, t.Peers) +} + +type RibEntry struct { + PeerIndex uint16 + OriginatedTime uint32 + PathAttributes []bgp.PathAttributeInterface +} + +func (e *RibEntry) DecodeFromBytes(data []byte) ([]byte, error) { + notAllBytesAvail := fmt.Errorf("not all RibEntry bytes are available") + if len(data) < 8 { + return nil, notAllBytesAvail + } + e.PeerIndex = binary.BigEndian.Uint16(data[:2]) + e.OriginatedTime = binary.BigEndian.Uint32(data[2:6]) + totalLen := binary.BigEndian.Uint16(data[6:8]) + data = data[8:] + for attrLen := totalLen; attrLen > 0; { + p, err := bgp.GetPathAttribute(data) + if err != nil { + return nil, err + } + err = p.DecodeFromBytes(data) + if err != nil { + return nil, err + } + attrLen -= uint16(p.Len()) + if len(data) < p.Len() { + return nil, notAllBytesAvail + } + data = data[p.Len():] + e.PathAttributes = append(e.PathAttributes, p) + } + return data, nil +} + +func (e *RibEntry) Serialize() ([]byte, error) { + buf := make([]byte, 8) + binary.BigEndian.PutUint16(buf, e.PeerIndex) + binary.BigEndian.PutUint32(buf[2:], e.OriginatedTime) + totalLen := 0 + binary.BigEndian.PutUint16(buf[6:], uint16(totalLen)) + for _, pattr := range e.PathAttributes { + // TODO special modification is needed for MP_REACH_NLRI + // but also Quagga doesn't implement this. + // + // RFC 6396 4.3.4 + // There is one exception to the encoding of BGP attributes for the BGP + // MP_REACH_NLRI attribute (BGP Type Code 14). + // Since the AFI, SAFI, and NLRI information is already encoded + // in the RIB Entry Header or RIB_GENERIC Entry Header, + // only the Next Hop Address Length and Next Hop Address fields are included. + + bbuf, err := pattr.Serialize() + if err != nil { + return nil, err + } + buf = append(buf, bbuf...) + totalLen += len(bbuf) + } + binary.BigEndian.PutUint16(buf[6:], uint16(totalLen)) + return buf, nil +} + +func NewRibEntry(index uint16, time uint32, pathattrs []bgp.PathAttributeInterface) *RibEntry { + return &RibEntry{ + PeerIndex: index, + OriginatedTime: time, + PathAttributes: pathattrs, + } +} + +func (e *RibEntry) String() string { + return fmt.Sprintf("RIB_ENTRY: PeerIndex [%d] OriginatedTime [%d] PathAttrs [%v]", e.PeerIndex, e.OriginatedTime, e.PathAttributes) +} + +type Rib struct { + SequenceNumber uint32 + Prefix bgp.AddrPrefixInterface + Entries []*RibEntry + RouteFamily bgp.RouteFamily +} + +func (u *Rib) DecodeFromBytes(data []byte) error { + if len(data) < 4 { + return fmt.Errorf("Not all RibIpv4Unicast message bytes available") + } + u.SequenceNumber = binary.BigEndian.Uint32(data[:4]) + data = data[4:] + afi, safi := bgp.RouteFamilyToAfiSafi(u.RouteFamily) + if afi == 0 && safi == 0 { + afi = binary.BigEndian.Uint16(data[:2]) + safi = data[2] + data = data[3:] + } + prefix, err := bgp.NewPrefixFromRouteFamily(afi, safi) + if err != nil { + return err + } + err = prefix.DecodeFromBytes(data) + if err != nil { + return err + } + u.Prefix = prefix + data = data[prefix.Len():] + entryNum := binary.BigEndian.Uint16(data[:2]) + data = data[2:] + u.Entries = make([]*RibEntry, 0, entryNum) + for i := 0; i < int(entryNum); i++ { + e := &RibEntry{} + data, err = e.DecodeFromBytes(data) + if err != nil { + return err + } + u.Entries = append(u.Entries, e) + } + return nil +} + +func (u *Rib) Serialize() ([]byte, error) { + buf := make([]byte, 4) + binary.BigEndian.PutUint32(buf, u.SequenceNumber) + rf := bgp.AfiSafiToRouteFamily(u.Prefix.AFI(), u.Prefix.SAFI()) + switch rf { + case bgp.RF_IPv4_UC, bgp.RF_IPv4_MC, bgp.RF_IPv6_UC, bgp.RF_IPv6_MC: + default: + bbuf := make([]byte, 0, 2) + binary.BigEndian.PutUint16(bbuf, u.Prefix.AFI()) + buf = append(buf, bbuf...) + buf = append(buf, u.Prefix.SAFI()) + } + bbuf, err := u.Prefix.Serialize() + if err != nil { + return nil, err + } + buf = append(buf, bbuf...) + bbuf, err = packValues([]interface{}{uint16(len(u.Entries))}) + if err != nil { + return nil, err + } + buf = append(buf, bbuf...) + for _, entry := range u.Entries { + bbuf, err = entry.Serialize() + if err != nil { + return nil, err + } + buf = append(buf, bbuf...) + } + return buf, nil +} + +func NewRib(seq uint32, prefix bgp.AddrPrefixInterface, entries []*RibEntry) *Rib { + rf := bgp.AfiSafiToRouteFamily(prefix.AFI(), prefix.SAFI()) + return &Rib{ + SequenceNumber: seq, + Prefix: prefix, + Entries: entries, + RouteFamily: rf, + } +} + +func (u *Rib) String() string { + return fmt.Sprintf("RIB: Seq [%d] Prefix [%s] Entries [%s]", u.SequenceNumber, u.Prefix, u.Entries) +} + +type BGP4MPHeader struct { + PeerAS uint32 + LocalAS uint32 + InterfaceIndex uint16 + AddressFamily uint16 + PeerIpAddress net.IP + LocalIpAddress net.IP + isAS4 bool +} + +func (m *BGP4MPHeader) decodeFromBytes(data []byte) ([]byte, error) { + if m.isAS4 && len(data) < 8 { + return nil, fmt.Errorf("Not all BGP4MPMessageAS4 bytes available") + } else if !m.isAS4 && len(data) < 4 { + return nil, fmt.Errorf("Not all BGP4MPMessageAS bytes available") + } + + if m.isAS4 { + m.PeerAS = binary.BigEndian.Uint32(data[:4]) + m.LocalAS = binary.BigEndian.Uint32(data[4:8]) + data = data[8:] + } else { + m.PeerAS = uint32(binary.BigEndian.Uint16(data[:2])) + m.LocalAS = uint32(binary.BigEndian.Uint16(data[2:4])) + data = data[4:] + } + m.InterfaceIndex = binary.BigEndian.Uint16(data[:2]) + m.AddressFamily = binary.BigEndian.Uint16(data[2:4]) + switch m.AddressFamily { + case bgp.AFI_IP: + m.PeerIpAddress = net.IP(data[4:8]).To4() + m.LocalIpAddress = net.IP(data[8:12]).To4() + data = data[12:] + case bgp.AFI_IP6: + m.PeerIpAddress = net.IP(data[4:20]) + m.LocalIpAddress = net.IP(data[20:36]) + data = data[36:] + default: + return nil, fmt.Errorf("unsupported address family: %d", m.AddressFamily) + } + return data, nil +} + +func (m *BGP4MPHeader) serialize() ([]byte, error) { + var values []interface{} + if m.isAS4 { + values = []interface{}{m.PeerAS, m.LocalAS, m.InterfaceIndex, m.AddressFamily} + } else { + values = []interface{}{uint16(m.PeerAS), uint16(m.LocalAS), m.InterfaceIndex, m.AddressFamily} + } + buf, err := packValues(values) + if err != nil { + return nil, err + } + var bbuf []byte + switch m.AddressFamily { + case bgp.AFI_IP: + bbuf = make([]byte, 8) + copy(bbuf, m.PeerIpAddress.To4()) + copy(bbuf[4:], m.LocalIpAddress.To4()) + case bgp.AFI_IP6: + bbuf = make([]byte, 32) + copy(bbuf, m.PeerIpAddress) + copy(bbuf[16:], m.LocalIpAddress) + default: + return nil, fmt.Errorf("unsupported address family: %d", m.AddressFamily) + } + return append(buf, bbuf...), nil +} + +func newBGP4MPHeader(peeras, localas uint32, intfindex uint16, peerip, localip string, isAS4 bool) (*BGP4MPHeader, error) { + var af uint16 + paddr := net.ParseIP(peerip).To4() + laddr := net.ParseIP(localip).To4() + if paddr != nil && laddr != nil { + af = bgp.AFI_IP + } else { + paddr = net.ParseIP(peerip).To16() + laddr = net.ParseIP(localip).To16() + if paddr != nil && laddr != nil { + af = bgp.AFI_IP6 + } else { + return nil, fmt.Errorf("Peer IP Address and Local IP Address must have the same address family") + } + } + return &BGP4MPHeader{ + PeerAS: peeras, + LocalAS: localas, + InterfaceIndex: intfindex, + AddressFamily: af, + PeerIpAddress: paddr, + LocalIpAddress: laddr, + isAS4: isAS4, + }, nil +} + +type BGP4MPStateChange struct { + *BGP4MPHeader + OldState BGPState + NewState BGPState +} + +func (m *BGP4MPStateChange) DecodeFromBytes(data []byte) error { + rest, err := m.decodeFromBytes(data) + if err != nil { + return err + } + if len(rest) < 4 { + return fmt.Errorf("Not all BGP4MPStateChange bytes available") + } + m.OldState = BGPState(binary.BigEndian.Uint16(rest[:2])) + m.NewState = BGPState(binary.BigEndian.Uint16(rest[2:4])) + return nil +} + +func (m *BGP4MPStateChange) Serialize() ([]byte, error) { + buf, err := m.serialize() + if err != nil { + return nil, err + } + bbuf, err := packValues([]interface{}{m.OldState, m.NewState}) + if err != nil { + return nil, err + } + return append(buf, bbuf...), nil +} + +func NewBGP4MPStateChange(peeras, localas uint32, intfindex uint16, peerip, localip string, isAS4 bool, oldstate, newstate BGPState) *BGP4MPStateChange { + header, _ := newBGP4MPHeader(peeras, localas, intfindex, peerip, localip, isAS4) + return &BGP4MPStateChange{ + BGP4MPHeader: header, + OldState: oldstate, + NewState: newstate, + } +} + +type BGP4MPMessage struct { + *BGP4MPHeader + BGPMessage *bgp.BGPMessage + BGPMessagePayload []byte + isLocal bool +} + +func (m *BGP4MPMessage) DecodeFromBytes(data []byte) error { + rest, err := m.decodeFromBytes(data) + if err != nil { + return err + } + + if len(rest) < bgp.BGP_HEADER_LENGTH { + return fmt.Errorf("Not all BGP4MPMessageAS4 bytes available") + } + + msg, err := bgp.ParseBGPMessage(rest) + if err != nil { + return err + } + m.BGPMessage = msg + return nil +} + +func (m *BGP4MPMessage) Serialize() ([]byte, error) { + buf, err := m.serialize() + if err != nil { + return nil, err + } + if m.BGPMessagePayload != nil { + return append(buf, m.BGPMessagePayload...), nil + } + bbuf, err := m.BGPMessage.Serialize() + if err != nil { + return nil, err + } + return append(buf, bbuf...), nil +} + +func NewBGP4MPMessage(peeras, localas uint32, intfindex uint16, peerip, localip string, isAS4 bool, msg *bgp.BGPMessage) *BGP4MPMessage { + header, _ := newBGP4MPHeader(peeras, localas, intfindex, peerip, localip, isAS4) + return &BGP4MPMessage{ + BGP4MPHeader: header, + BGPMessage: msg, + } +} + +func NewBGP4MPMessageLocal(peeras, localas uint32, intfindex uint16, peerip, localip string, isAS4 bool, msg *bgp.BGPMessage) *BGP4MPMessage { + header, _ := newBGP4MPHeader(peeras, localas, intfindex, peerip, localip, isAS4) + return &BGP4MPMessage{ + BGP4MPHeader: header, + BGPMessage: msg, + isLocal: true, + } +} + +func (m *BGP4MPMessage) String() string { + title := "BGP4MP_MSG" + if m.isAS4 { + title += "_AS4" + } + if m.isLocal { + title += "_LOCAL" + } + return fmt.Sprintf("%s: PeerAS [%d] LocalAS [%d] InterfaceIndex [%d] PeerIP [%s] LocalIP [%s] BGPMessage [%v]", title, m.PeerAS, m.LocalAS, m.InterfaceIndex, m.PeerIpAddress, m.LocalIpAddress, m.BGPMessage) +} + +//This function can be passed into a bufio.Scanner.Split() to read buffered mrt msgs +func SplitMrt(data []byte, atEOF bool) (advance int, token []byte, err error) { + if atEOF && len(data) == 0 { + return 0, nil, nil + } + if cap(data) < MRT_COMMON_HEADER_LEN { // read more + return 0, nil, nil + } + //this reads the data + hdr := &MRTHeader{} + errh := hdr.DecodeFromBytes(data[:MRT_COMMON_HEADER_LEN]) + if errh != nil { + return 0, nil, errh + } + totlen := int(hdr.Len + MRT_COMMON_HEADER_LEN) + if len(data) < totlen { //need to read more + return 0, nil, nil + } + return totlen, data[0:totlen], nil +} + +func ParseMRTBody(h *MRTHeader, data []byte) (*MRTMessage, error) { + if len(data) < int(h.Len) { + return nil, fmt.Errorf("Not all MRT message bytes available. expected: %d, actual: %d", int(h.Len), len(data)) + } + msg := &MRTMessage{Header: *h} + switch h.Type { + case TABLE_DUMPv2: + subType := MRTSubTypeTableDumpv2(h.SubType) + rf := bgp.RouteFamily(0) + switch subType { + case PEER_INDEX_TABLE: + msg.Body = &PeerIndexTable{} + case RIB_IPV4_UNICAST: + rf = bgp.RF_IPv4_UC + case RIB_IPV4_MULTICAST: + rf = bgp.RF_IPv4_MC + case RIB_IPV6_UNICAST: + rf = bgp.RF_IPv6_UC + case RIB_IPV6_MULTICAST: + rf = bgp.RF_IPv6_MC + case RIB_GENERIC: + default: + return nil, fmt.Errorf("unsupported table dumpv2 subtype: %v\n", subType) + } + + if subType != PEER_INDEX_TABLE { + msg.Body = &Rib{ + RouteFamily: rf, + } + } + case BGP4MP: + subType := MRTSubTypeBGP4MP(h.SubType) + isAS4 := true + switch subType { + case STATE_CHANGE: + isAS4 = false + fallthrough + case STATE_CHANGE_AS4: + msg.Body = &BGP4MPStateChange{ + BGP4MPHeader: &BGP4MPHeader{isAS4: isAS4}, + } + case MESSAGE: + isAS4 = false + fallthrough + case MESSAGE_AS4: + msg.Body = &BGP4MPMessage{ + BGP4MPHeader: &BGP4MPHeader{isAS4: isAS4}, + } + case MESSAGE_LOCAL: + isAS4 = false + fallthrough + case MESSAGE_AS4_LOCAL: + msg.Body = &BGP4MPMessage{ + BGP4MPHeader: &BGP4MPHeader{isAS4: isAS4}, + isLocal: true, + } + default: + return nil, fmt.Errorf("unsupported bgp4mp subtype: %v\n", subType) + } + default: + return nil, fmt.Errorf("unsupported type: %v\n", h.Type) + } + err := msg.Body.DecodeFromBytes(data) + if err != nil { + return nil, err + } + return msg, nil +} diff --git a/packet/mrt/mrt_test.go b/packet/mrt/mrt_test.go new file mode 100644 index 00000000..634d69ae --- /dev/null +++ b/packet/mrt/mrt_test.go @@ -0,0 +1,221 @@ +// 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 mrt + +import ( + "bufio" + "bytes" + "github.com/osrg/gobgp/packet/bgp" + "github.com/stretchr/testify/assert" + "reflect" + "testing" + "time" +) + +func TestMrtHdr(t *testing.T) { + h1, err := NewMRTHeader(10, TABLE_DUMPv2, RIB_IPV4_MULTICAST, 20) + if err != nil { + t.Fatal(err) + } + b1, err := h1.Serialize() + if err != nil { + t.Fatal(err) + } + h2 := &MRTHeader{} + err = h2.DecodeFromBytes(b1) + if err != nil { + t.Fatal(err) + } + assert.Equal(t, reflect.DeepEqual(h1, h2), true) +} + +func TestMrtHdrTime(t *testing.T) { + h1, err := NewMRTHeader(10, TABLE_DUMPv2, RIB_IPV4_MULTICAST, 20) + if err != nil { + t.Fatal(err) + } + ttime := time.Unix(10, 0) + htime := h1.GetTime() + t.Logf("this timestamp should be 10s after epoch:%v", htime) + assert.Equal(t, h1.GetTime(), ttime) +} + +func testPeer(t *testing.T, p1 *Peer) { + b1, err := p1.Serialize() + if err != nil { + t.Fatal(err) + } + p2 := &Peer{} + rest, err := p2.DecodeFromBytes(b1) + if err != nil { + t.Fatal(err) + } + assert.Equal(t, len(rest), 0) + assert.Equal(t, reflect.DeepEqual(p1, p2), true) +} + +func TestMrtPeer(t *testing.T) { + p := NewPeer("192.168.0.1", "10.0.0.1", 65000, false) + testPeer(t, p) +} + +func TestMrtPeerv6(t *testing.T) { + p := NewPeer("192.168.0.1", "2001::1", 65000, false) + testPeer(t, p) +} + +func TestMrtPeerAS4(t *testing.T) { + p := NewPeer("192.168.0.1", "2001::1", 135500, true) + testPeer(t, p) +} + +func TestMrtPeerIndexTable(t *testing.T) { + p1 := NewPeer("192.168.0.1", "10.0.0.1", 65000, false) + p2 := NewPeer("192.168.0.1", "2001::1", 65000, false) + p3 := NewPeer("192.168.0.1", "2001::1", 135500, true) + pt1 := NewPeerIndexTable("192.168.0.1", "test", []*Peer{p1, p2, p3}) + b1, err := pt1.Serialize() + if err != nil { + t.Fatal(err) + } + pt2 := &PeerIndexTable{} + err = pt2.DecodeFromBytes(b1) + if err != nil { + t.Fatal(err) + } + assert.Equal(t, reflect.DeepEqual(pt1, pt2), true) +} + +func TestMrtRibEntry(t *testing.T) { + aspath1 := []bgp.AsPathParamInterface{ + bgp.NewAsPathParam(2, []uint16{1000}), + bgp.NewAsPathParam(1, []uint16{1001, 1002}), + bgp.NewAsPathParam(2, []uint16{1003, 1004}), + } + + p := []bgp.PathAttributeInterface{ + bgp.NewPathAttributeOrigin(3), + bgp.NewPathAttributeAsPath(aspath1), + bgp.NewPathAttributeNextHop("129.1.1.2"), + bgp.NewPathAttributeMultiExitDisc(1 << 20), + bgp.NewPathAttributeLocalPref(1 << 22), + } + + e1 := NewRibEntry(1, uint32(time.Now().Unix()), p) + b1, err := e1.Serialize() + if err != nil { + t.Fatal(err) + } + + e2 := &RibEntry{} + rest, err := e2.DecodeFromBytes(b1) + if err != nil { + t.Fatal(err) + } + assert.Equal(t, len(rest), 0) + assert.Equal(t, reflect.DeepEqual(e1, e2), true) +} + +func TestMrtRib(t *testing.T) { + aspath1 := []bgp.AsPathParamInterface{ + bgp.NewAsPathParam(2, []uint16{1000}), + bgp.NewAsPathParam(1, []uint16{1001, 1002}), + bgp.NewAsPathParam(2, []uint16{1003, 1004}), + } + + p := []bgp.PathAttributeInterface{ + bgp.NewPathAttributeOrigin(3), + bgp.NewPathAttributeAsPath(aspath1), + bgp.NewPathAttributeNextHop("129.1.1.2"), + bgp.NewPathAttributeMultiExitDisc(1 << 20), + bgp.NewPathAttributeLocalPref(1 << 22), + } + + e1 := NewRibEntry(1, uint32(time.Now().Unix()), p) + e2 := NewRibEntry(2, uint32(time.Now().Unix()), p) + e3 := NewRibEntry(3, uint32(time.Now().Unix()), p) + + r1 := NewRib(1, bgp.NewIPAddrPrefix(24, "192.168.0.0"), []*RibEntry{e1, e2, e3}) + b1, err := r1.Serialize() + if err != nil { + t.Fatal(err) + } + r2 := &Rib{ + RouteFamily: bgp.RF_IPv4_UC, + } + err = r2.DecodeFromBytes(b1) + if err != nil { + t.Fatal(err) + } + assert.Equal(t, reflect.DeepEqual(r1, r2), true) +} + +func TestMrtBgp4mpStateChange(t *testing.T) { + c1 := NewBGP4MPStateChange(65000, 65001, 1, "192.168.0.1", "192.168.0.2", false, ACTIVE, ESTABLISHED) + b1, err := c1.Serialize() + if err != nil { + t.Fatal(err) + } + c2 := &BGP4MPStateChange{BGP4MPHeader: &BGP4MPHeader{}} + err = c2.DecodeFromBytes(b1) + if err != nil { + t.Fatal(err) + } + _, err = c2.Serialize() + if err != nil { + t.Fatal(err) + } + assert.Equal(t, reflect.DeepEqual(c1, c2), true) +} + +func TestMrtBgp4mpMessage(t *testing.T) { + msg := bgp.NewBGPKeepAliveMessage() + m1 := NewBGP4MPMessage(65000, 65001, 1, "192.168.0.1", "192.168.0.2", false, msg) + b1, err := m1.Serialize() + if err != nil { + t.Fatal(err) + } + m2 := &BGP4MPMessage{BGP4MPHeader: &BGP4MPHeader{}} + err = m2.DecodeFromBytes(b1) + if err != nil { + t.Fatal(err) + } + assert.Equal(t, reflect.DeepEqual(m1, m2), true) +} + +func TestMrtSplit(t *testing.T) { + var b bytes.Buffer + numwrite, numread := 10, 0 + for i := 0; i < numwrite; i++ { + msg := bgp.NewBGPKeepAliveMessage() + m1 := NewBGP4MPMessage(65000, 65001, 1, "192.168.0.1", "192.168.0.2", false, msg) + mm, _ := NewMRTMessage(1234, BGP4MP, MESSAGE, m1) + b1, err := mm.Serialize() + if err != nil { + t.Fatal(err) + } + b.Write(b1) + } + t.Logf("wrote %d serialized MRT keepalives in the buffer", numwrite) + r := bytes.NewReader(b.Bytes()) + scanner := bufio.NewScanner(r) + scanner.Split(SplitMrt) + for scanner.Scan() { + numread += 1 + } + t.Logf("scanner scanned %d serialized keepalives from the buffer", numread) + assert.Equal(t, numwrite, numread) +} -- cgit v1.2.3