diff options
author | FUJITA Tomonori <fujita.tomonori@lab.ntt.co.jp> | 2018-07-07 13:48:38 +0900 |
---|---|---|
committer | FUJITA Tomonori <fujita.tomonori@lab.ntt.co.jp> | 2018-07-07 20:44:25 +0900 |
commit | c4775c42510d1f1ddd55036dc19e982712fa6a0b (patch) | |
tree | 6ec8b61d4338c809e239e3003a2d32d480898e22 /pkg/packet/mrt/mrt.go | |
parent | b3079759aa13172fcb548a83da9a9653d8d5fed4 (diff) |
follow Standard Go Project Layout
https://github.com/golang-standards/project-layout
Now you can see clearly what are private and public library code.
Signed-off-by: FUJITA Tomonori <fujita.tomonori@lab.ntt.co.jp>
Diffstat (limited to 'pkg/packet/mrt/mrt.go')
-rw-r--r-- | pkg/packet/mrt/mrt.go | 1006 |
1 files changed, 1006 insertions, 0 deletions
diff --git a/pkg/packet/mrt/mrt.go b/pkg/packet/mrt/mrt.go new file mode 100644 index 00000000..dc07ba9b --- /dev/null +++ b/pkg/packet/mrt/mrt.go @@ -0,0 +1,1006 @@ +// 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" + "math" + "net" + "time" + + "github.com/osrg/gobgp/pkg/packet/bgp" +) + +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 + GEO_PEER_TABLE MRTSubTypeTableDumpv2 = 7 // RFC6397 + RIB_IPV4_UNICAST_ADDPATH MRTSubTypeTableDumpv2 = 8 // RFC8050 + RIB_IPV4_MULTICAST_ADDPATH MRTSubTypeTableDumpv2 = 9 // RFC8050 + RIB_IPV6_UNICAST_ADDPATH MRTSubTypeTableDumpv2 = 10 // RFC8050 + RIB_IPV6_MULTICAST_ADDPATH MRTSubTypeTableDumpv2 = 11 // RFC8050 + RIB_GENERIC_ADDPATH MRTSubTypeTableDumpv2 = 12 // RFC8050 +) + +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 + MESSAGE_ADDPATH MRTSubTypeBGP4MP = 8 // RFC8050 + MESSAGE_AS4_ADDPATH MRTSubTypeBGP4MP = 9 // RFC8050 + MESSAGE_LOCAL_ADDPATH MRTSubTypeBGP4MP = 10 // RFC8050 + MESSAGE_AS4_LOCAL_ADDPATH MRTSubTypeBGP4MP = 11 // RFC8050 +) + +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 + PathIdentifier uint32 + PathAttributes []bgp.PathAttributeInterface + isAddPath bool +} + +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]) + if e.isAddPath { + e.PathIdentifier = binary.BigEndian.Uint32(data[6:10]) + data = data[10:] + } else { + data = data[6:] + } + totalLen := binary.BigEndian.Uint16(data[:2]) + data = data[2:] + 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) { + pbuf := make([]byte, 0) + totalLen := 0 + 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. + + pb, err := pattr.Serialize() + if err != nil { + return nil, err + } + pbuf = append(pbuf, pb...) + totalLen += len(pb) + } + var buf []byte + if e.isAddPath { + buf = make([]byte, 12) + binary.BigEndian.PutUint16(buf, e.PeerIndex) + binary.BigEndian.PutUint32(buf[2:], e.OriginatedTime) + binary.BigEndian.PutUint32(buf[6:], e.PathIdentifier) + binary.BigEndian.PutUint16(buf[10:], uint16(totalLen)) + } else { + buf = make([]byte, 8) + binary.BigEndian.PutUint16(buf, e.PeerIndex) + binary.BigEndian.PutUint32(buf[2:], e.OriginatedTime) + binary.BigEndian.PutUint16(buf[6:], uint16(totalLen)) + } + buf = append(buf, pbuf...) + return buf, nil +} + +func NewRibEntry(index uint16, time uint32, pathId uint32, pathAttrs []bgp.PathAttributeInterface, isAddPath bool) *RibEntry { + return &RibEntry{ + PeerIndex: index, + OriginatedTime: time, + PathIdentifier: pathId, + PathAttributes: pathAttrs, + isAddPath: isAddPath, + } +} + +func (e *RibEntry) String() string { + if e.isAddPath { + return fmt.Sprintf("RIB_ENTRY: PeerIndex [%d] OriginatedTime [%d] PathIdentifier[%d] PathAttributes [%v]", e.PeerIndex, e.OriginatedTime, e.PathIdentifier, e.PathAttributes) + } else { + return fmt.Sprintf("RIB_ENTRY: PeerIndex [%d] OriginatedTime [%d] PathAttributes [%v]", e.PeerIndex, e.OriginatedTime, e.PathAttributes) + } + +} + +type Rib struct { + SequenceNumber uint32 + Prefix bgp.AddrPrefixInterface + Entries []*RibEntry + RouteFamily bgp.RouteFamily + isAddPath bool +} + +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{ + isAddPath: u.isAddPath, + } + 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, 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, + isAddPath: entries[0].isAddPath, + } +} + +func (u *Rib) String() string { + return fmt.Sprintf("RIB: Seq [%d] Prefix [%s] Entries [%s]", u.SequenceNumber, u.Prefix, u.Entries) +} + +type GeoPeer struct { + Type uint8 + BgpId net.IP + Latitude float32 + Longitude float32 +} + +func (p *GeoPeer) DecodeFromBytes(data []byte) ([]byte, error) { + if len(data) < 13 { + return nil, fmt.Errorf("not all GeoPeer bytes are available") + } + // Peer IP Address and Peer AS should not be included + p.Type = uint8(data[0]) + if p.Type != uint8(0) { + return nil, fmt.Errorf("unsupported peer type for GeoPeer: %d", p.Type) + } + p.BgpId = net.IP(data[1:5]) + p.Latitude = math.Float32frombits(binary.BigEndian.Uint32(data[5:9])) + p.Longitude = math.Float32frombits(binary.BigEndian.Uint32(data[9:13])) + return data[13:], nil +} + +func (p *GeoPeer) Serialize() ([]byte, error) { + buf := make([]byte, 13) + buf[0] = uint8(0) // Peer IP Address and Peer AS should not be included + bgpId := p.BgpId.To4() + if bgpId == nil { + return nil, fmt.Errorf("invalid BgpId: %s", p.BgpId) + } + copy(buf[1:5], bgpId) + binary.BigEndian.PutUint32(buf[5:9], math.Float32bits(p.Latitude)) + binary.BigEndian.PutUint32(buf[9:13], math.Float32bits(p.Longitude)) + return buf, nil +} + +func NewGeoPeer(bgpid string, latitude float32, longitude float32) *GeoPeer { + return &GeoPeer{ + Type: 0, // Peer IP Address and Peer AS should not be included + BgpId: net.ParseIP(bgpid).To4(), + Latitude: latitude, + Longitude: longitude, + } +} + +func (p *GeoPeer) String() string { + return fmt.Sprintf("PEER ENTRY: ID [%s] Latitude [%f] Longitude [%f]", p.BgpId, p.Latitude, p.Longitude) +} + +type GeoPeerTable struct { + CollectorBgpId net.IP + CollectorLatitude float32 + CollectorLongitude float32 + Peers []*GeoPeer +} + +func (t *GeoPeerTable) DecodeFromBytes(data []byte) error { + if len(data) < 14 { + return fmt.Errorf("not all GeoPeerTable bytes are available") + } + t.CollectorBgpId = net.IP(data[0:4]) + t.CollectorLatitude = math.Float32frombits(binary.BigEndian.Uint32(data[4:8])) + t.CollectorLongitude = math.Float32frombits(binary.BigEndian.Uint32(data[8:12])) + peerCount := binary.BigEndian.Uint16(data[12:14]) + data = data[14:] + t.Peers = make([]*GeoPeer, 0, peerCount) + var err error + for i := 0; i < int(peerCount); i++ { + p := &GeoPeer{} + if data, err = p.DecodeFromBytes(data); err != nil { + return err + } + t.Peers = append(t.Peers, p) + } + return nil +} + +func (t *GeoPeerTable) Serialize() ([]byte, error) { + buf := make([]byte, 14) + collectorBgpId := t.CollectorBgpId.To4() + if collectorBgpId == nil { + return nil, fmt.Errorf("invalid CollectorBgpId: %s", t.CollectorBgpId) + } + copy(buf[0:4], collectorBgpId) + binary.BigEndian.PutUint32(buf[4:8], math.Float32bits(t.CollectorLatitude)) + binary.BigEndian.PutUint32(buf[8:12], math.Float32bits(t.CollectorLongitude)) + binary.BigEndian.PutUint16(buf[12:14], uint16(len(t.Peers))) + for _, peer := range t.Peers { + pbuf, err := peer.Serialize() + if err != nil { + return nil, err + } + buf = append(buf, pbuf...) + } + return buf, nil +} + +func NewGeoPeerTable(bgpid string, latitude float32, longitude float32, peers []*GeoPeer) *GeoPeerTable { + return &GeoPeerTable{ + CollectorBgpId: net.ParseIP(bgpid).To4(), + CollectorLatitude: latitude, + CollectorLongitude: longitude, + Peers: peers, + } +} + +func (t *GeoPeerTable) String() string { + return fmt.Sprintf("GEO_PEER_TABLE: CollectorBgpId [%s] CollectorLatitude [%f] CollectorLongitude [%f] Peers [%s]", t.CollectorBgpId, t.CollectorLatitude, t.CollectorLongitude, t.Peers) +} + +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 + isAddPath 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 NewBGP4MPMessageAddPath(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, + isAddPath: true, + } +} + +func NewBGP4MPMessageLocalAddPath(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, + isAddPath: true, + } +} + +func (m *BGP4MPMessage) String() string { + title := "BGP4MP_MSG" + if m.isAS4 { + title += "_AS4" + } + if m.isLocal { + title += "_LOCAL" + } + if m.isAddPath { + title += "_ADDPATH" + } + 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) + isAddPath := false + 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: + case GEO_PEER_TABLE: + msg.Body = &GeoPeerTable{} + case RIB_IPV4_UNICAST_ADDPATH: + rf = bgp.RF_IPv4_UC + isAddPath = true + case RIB_IPV4_MULTICAST_ADDPATH: + rf = bgp.RF_IPv4_MC + isAddPath = true + case RIB_IPV6_UNICAST_ADDPATH: + rf = bgp.RF_IPv6_UC + isAddPath = true + case RIB_IPV6_MULTICAST_ADDPATH: + rf = bgp.RF_IPv6_MC + isAddPath = true + case RIB_GENERIC_ADDPATH: + isAddPath = true + default: + return nil, fmt.Errorf("unsupported table dumpv2 subtype: %v\n", subType) + } + + if msg.Body == nil { + msg.Body = &Rib{ + RouteFamily: rf, + isAddPath: isAddPath, + } + } + 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, + } + case MESSAGE_ADDPATH: + isAS4 = false + fallthrough + case MESSAGE_AS4_ADDPATH: + msg.Body = &BGP4MPMessage{ + BGP4MPHeader: &BGP4MPHeader{isAS4: isAS4}, + isAddPath: true, + } + case MESSAGE_LOCAL_ADDPATH: + isAS4 = false + fallthrough + case MESSAGE_AS4_LOCAL_ADDPATH: + msg.Body = &BGP4MPMessage{ + BGP4MPHeader: &BGP4MPHeader{isAS4: isAS4}, + isLocal: true, + isAddPath: 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 +} |