386 lines
8.8 KiB
Go
386 lines
8.8 KiB
Go
package messages
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/binary"
|
|
"errors"
|
|
"fmt"
|
|
log "github.com/sirupsen/logrus"
|
|
"io"
|
|
"net"
|
|
)
|
|
|
|
type BGPCapability_MP struct {
|
|
Afi uint16
|
|
Safi byte
|
|
}
|
|
|
|
type BGPCapability_ASN struct {
|
|
ASN uint32
|
|
}
|
|
|
|
type AddPath struct {
|
|
Afi uint16
|
|
Safi byte
|
|
TxRx byte
|
|
}
|
|
|
|
type BGPCapability_ROUTEREFRESH struct {
|
|
}
|
|
|
|
type BGPCapability_ADDPATH struct {
|
|
AddPathList []AddPath
|
|
}
|
|
|
|
type BGPCapability struct {
|
|
Type byte
|
|
Data []byte
|
|
}
|
|
|
|
type BGPCapabilityIf SerializableInterface
|
|
|
|
type BGPCapabilities struct {
|
|
BGPCapabilities []BGPCapabilityIf
|
|
}
|
|
|
|
type BGPParameterIf SerializableInterface
|
|
|
|
type BGPParameter struct {
|
|
Type byte
|
|
Data BGPParameterIf
|
|
}
|
|
|
|
type BGPMessageOpen struct {
|
|
BGPMessageHead
|
|
Version byte
|
|
ASN uint16
|
|
HoldTime uint16
|
|
Identifier []byte
|
|
Parameters []BGPParameter
|
|
}
|
|
|
|
func (m BGPParameter) String() string {
|
|
str := "Parameter (%v):"
|
|
str += m.Data.String()
|
|
return fmt.Sprintf(str, m.Type)
|
|
}
|
|
|
|
func (m BGPCapability) String() string {
|
|
str := "Capability: %v (%v): %v"
|
|
return fmt.Sprintf(str, CapaDescr[int(m.Type)], m.Type, m.Data)
|
|
}
|
|
|
|
func (c BGPCapability_ROUTEREFRESH) String() string {
|
|
return "Capability Route-Refresh"
|
|
}
|
|
|
|
func (c BGPCapability_MP) String() string {
|
|
return fmt.Sprintf("Capability Multiprotocol: %v-%v (%v) (%v)", AfiToStr[c.Afi], SafiToStr[c.Safi], c.Afi, c.Safi)
|
|
}
|
|
|
|
func (c BGPCapability_ADDPATH) String() string {
|
|
var addpathstr string
|
|
for i := range c.AddPathList {
|
|
addpathstr += c.AddPathList[i].String() + ", "
|
|
}
|
|
return fmt.Sprintf("Capability Add-Path: [ %v]", addpathstr)
|
|
}
|
|
|
|
func (c BGPCapability_ASN) String() string {
|
|
return fmt.Sprintf("Capability ASN: %v", c.ASN)
|
|
}
|
|
|
|
func (c BGPCapabilities) String() string {
|
|
var str string
|
|
for i := range c.BGPCapabilities {
|
|
if c.BGPCapabilities[i] != nil {
|
|
str += c.BGPCapabilities[i].String() + ", "
|
|
}
|
|
}
|
|
return str
|
|
}
|
|
|
|
func (m *BGPMessageOpen) String() string {
|
|
str := "BGP Open: Version: %v / ASN: %v / HoldTime: %v / Identifier: %v / Parameters (%v): [ "
|
|
ip := net.IP(m.Identifier)
|
|
str = fmt.Sprintf(str, m.Version, m.ASN, m.HoldTime, ip.String(), len(m.Parameters))
|
|
for i := range m.Parameters {
|
|
str += m.Parameters[i].String()
|
|
}
|
|
str += "]"
|
|
return str
|
|
}
|
|
|
|
func (m BGPCapabilities) Len() int {
|
|
var sum int
|
|
for c := range m.BGPCapabilities {
|
|
sum += m.BGPCapabilities[c].Len()
|
|
}
|
|
return sum
|
|
}
|
|
|
|
func (c BGPCapabilities) Write(bw io.Writer) {
|
|
for i := range c.BGPCapabilities {
|
|
c.BGPCapabilities[i].Write(bw)
|
|
}
|
|
}
|
|
|
|
func (m BGPCapability_ROUTEREFRESH) Len() int {
|
|
return 2
|
|
}
|
|
|
|
func (m BGPCapability_ROUTEREFRESH) Write(bw io.Writer) {
|
|
binary.Write(bw, binary.BigEndian, byte(CAPA_RR))
|
|
binary.Write(bw, binary.BigEndian, byte(0))
|
|
}
|
|
|
|
func (m BGPCapability_MP) Len() int {
|
|
return 6
|
|
}
|
|
|
|
func (m BGPCapability_MP) Write(bw io.Writer) {
|
|
binary.Write(bw, binary.BigEndian, byte(CAPA_MP))
|
|
binary.Write(bw, binary.BigEndian, byte(4))
|
|
|
|
binary.Write(bw, binary.BigEndian, m.Afi)
|
|
binary.Write(bw, binary.BigEndian, byte(0))
|
|
binary.Write(bw, binary.BigEndian, m.Safi)
|
|
}
|
|
|
|
func (m BGPCapability_ASN) Len() int {
|
|
return 6
|
|
}
|
|
|
|
func (m BGPCapability_ASN) Write(bw io.Writer) {
|
|
binary.Write(bw, binary.BigEndian, byte(CAPA_ASN))
|
|
binary.Write(bw, binary.BigEndian, byte(4))
|
|
|
|
binary.Write(bw, binary.BigEndian, m.ASN)
|
|
}
|
|
|
|
func (p AddPath) Len() int {
|
|
return 4
|
|
}
|
|
|
|
func (p AddPath) Write(bw io.Writer) {
|
|
binary.Write(bw, binary.BigEndian, p.Afi)
|
|
binary.Write(bw, binary.BigEndian, p.Safi)
|
|
binary.Write(bw, binary.BigEndian, p.TxRx)
|
|
}
|
|
|
|
func (m BGPCapability_ADDPATH) Len() int {
|
|
var sum int
|
|
for c := range m.AddPathList {
|
|
sum += m.AddPathList[c].Len()
|
|
}
|
|
return sum
|
|
}
|
|
|
|
func (m BGPCapability_ADDPATH) Write(bw io.Writer) {
|
|
for c := range m.AddPathList {
|
|
m.AddPathList[c].Write(bw)
|
|
}
|
|
}
|
|
|
|
func (m BGPCapability) Len() int {
|
|
return 1 + 1 + len(m.Data)
|
|
}
|
|
|
|
func (m BGPCapability) Write(bw io.Writer) {
|
|
binary.Write(bw, binary.BigEndian, m.Type)
|
|
binary.Write(bw, binary.BigEndian, byte(len(m.Data)))
|
|
binary.Write(bw, binary.BigEndian, m.Data)
|
|
}
|
|
|
|
func (m BGPParameter) Len() int {
|
|
return 1 + 1 + m.Data.Len()
|
|
}
|
|
|
|
func (m BGPParameter) Write(bw io.Writer) {
|
|
binary.Write(bw, binary.BigEndian, m.Type)
|
|
binary.Write(bw, binary.BigEndian, byte(m.Data.Len()))
|
|
m.Data.Write(bw)
|
|
}
|
|
|
|
func (m BGPMessageOpen) LenParams() int {
|
|
sum := 0
|
|
for i := range m.Parameters {
|
|
sum += m.Parameters[i].Len()
|
|
}
|
|
return sum
|
|
}
|
|
|
|
func (m BGPMessageOpen) LenContent() int {
|
|
return 10 + m.LenParams()
|
|
}
|
|
|
|
func (m BGPMessageOpen) Len() int {
|
|
return GetBGPHeaderLen() + m.LenContent()
|
|
}
|
|
|
|
func (m BGPMessageOpen) Write(bw io.Writer) {
|
|
WriteBGPHeader(MESSAGE_OPEN, uint16(m.LenContent()), bw)
|
|
binary.Write(bw, binary.BigEndian, m.Version)
|
|
binary.Write(bw, binary.BigEndian, m.ASN)
|
|
binary.Write(bw, binary.BigEndian, m.HoldTime)
|
|
binary.Write(bw, binary.BigEndian, m.Identifier[0:4])
|
|
binary.Write(bw, binary.BigEndian, byte(m.LenParams()))
|
|
for i := range m.Parameters {
|
|
m.Parameters[i].Write(bw)
|
|
}
|
|
}
|
|
|
|
func (c BGPCapability) ParseCapability() BGPCapabilityIf {
|
|
buf := bytes.NewBuffer(c.Data)
|
|
var ret BGPCapabilityIf
|
|
switch c.Type {
|
|
case CAPA_MP:
|
|
mpstruct := BGPCapability_MP{}
|
|
binary.Read(buf, binary.BigEndian, &mpstruct.Afi)
|
|
buf.ReadByte()
|
|
binary.Read(buf, binary.BigEndian, &mpstruct.Safi)
|
|
ret = mpstruct
|
|
case CAPA_ADDPATH:
|
|
apstruct := BGPCapability_ADDPATH{
|
|
AddPathList: make([]AddPath, len(c.Data)/4),
|
|
}
|
|
for i := 0; i < len(c.Data)/4; i++ {
|
|
binary.Read(buf, binary.BigEndian, &(apstruct.AddPathList[i].Afi))
|
|
binary.Read(buf, binary.BigEndian, &(apstruct.AddPathList[i].Safi))
|
|
binary.Read(buf, binary.BigEndian, &(apstruct.AddPathList[i].TxRx))
|
|
}
|
|
|
|
ret = apstruct
|
|
case CAPA_ASN:
|
|
asnstruct := BGPCapability_ASN{}
|
|
binary.Read(buf, binary.BigEndian, &asnstruct.ASN)
|
|
ret = asnstruct
|
|
case CAPA_RR:
|
|
ret = BGPCapability_ROUTEREFRESH{}
|
|
default:
|
|
unknownstruct := BGPCapability{}
|
|
unknownstruct.Type = c.Type
|
|
unknownstruct.Data = c.Data
|
|
ret = unknownstruct
|
|
}
|
|
return ret
|
|
}
|
|
|
|
func ParseOpen(b []byte) (*BGPMessageOpen, error) {
|
|
m := BGPMessageOpen{}
|
|
|
|
if len(b) < 10 {
|
|
return nil, errors.New(fmt.Sprintf("ParseOpen: wrong open size: %v < 10", len(b)))
|
|
}
|
|
version := b[0]
|
|
asn := uint16(b[1])<<8 | uint16(b[2])
|
|
holdtime := uint16(b[3])<<8 | uint16(b[4])
|
|
|
|
if holdtime > 0 && holdtime < 3 {
|
|
log.Warnf("ParseOpen: BGP open hold time must be zero or at least 3. Got %v.", holdtime)
|
|
}
|
|
|
|
identifier := b[5:9]
|
|
optparamlen := int(b[9])
|
|
|
|
m.Version = version
|
|
m.HoldTime = holdtime
|
|
m.Identifier = identifier
|
|
m.ASN = asn
|
|
m.Parameters = make([]BGPParameter, 0)
|
|
|
|
if len(b)-10 != optparamlen {
|
|
return nil, errors.New(fmt.Sprintf("ParseOpen: wrong open size for optional parameters: %v != %v", len(b)-10, optparamlen))
|
|
}
|
|
|
|
if optparamlen > 0 && len(b)-10 >= 2 {
|
|
i := 10
|
|
for i < len(b)-1 {
|
|
parmtype := b[i]
|
|
parmlength := int(b[i+1])
|
|
|
|
if i+1+parmlength > len(b) {
|
|
return nil, errors.New(fmt.Sprintf("ParseOpen: wrong parameter length: %v > %v", i+1+parmlength, len(b)))
|
|
}
|
|
|
|
i += 2
|
|
bgpparameter := BGPParameter{
|
|
Type: parmtype,
|
|
}
|
|
|
|
var parameterdata BGPCapabilityIf
|
|
baseparam := i
|
|
|
|
switch parmtype {
|
|
case PARAMETER_CAPA:
|
|
bgpcapa := BGPCapabilities{make([]BGPCapabilityIf, 0)}
|
|
for i < len(b)-1 && i < baseparam+parmlength {
|
|
capatype := b[i]
|
|
capalength := int(b[i+1])
|
|
|
|
if i+1+capalength > len(b) {
|
|
return nil, errors.New(fmt.Sprintf("ParseOpen: wrong capability length: %v > %v", i+1+capalength, len(b)))
|
|
}
|
|
capa := b[i+2 : i+2+capalength]
|
|
//log.Debugf("ParseOpen: Capa %v %v %v", capatype, capalength, capa)
|
|
|
|
i += 2 + capalength
|
|
|
|
capastruct := BGPCapability{
|
|
Type: capatype,
|
|
Data: capa,
|
|
}
|
|
capastructparsed := capastruct.ParseCapability()
|
|
bgpcapa.BGPCapabilities = append(bgpcapa.BGPCapabilities, capastructparsed)
|
|
}
|
|
parameterdata = bgpcapa
|
|
}
|
|
|
|
bgpparameter.Data = parameterdata
|
|
m.Parameters = append(m.Parameters, bgpparameter)
|
|
}
|
|
|
|
}
|
|
return &m, nil
|
|
}
|
|
|
|
func CraftOpenMessage(asn uint32, holdtime uint16, identifier []byte, mplist []BGPCapability_MP, addpathlist []AddPath, routerefresh bool) *BGPMessageOpen {
|
|
asn_2o := uint16(asn)
|
|
if asn >= 65536 {
|
|
asn_2o = 23456
|
|
}
|
|
// Check for identifier = 4: "bytes",
|
|
open := &BGPMessageOpen{
|
|
Version: 4,
|
|
ASN: asn_2o,
|
|
HoldTime: holdtime,
|
|
Identifier: identifier,
|
|
Parameters: make([]BGPParameter, 1)}
|
|
|
|
ptr := &BGPCapabilities{make([]BGPCapabilityIf, 0)}
|
|
ptr.BGPCapabilities = append(ptr.BGPCapabilities, &BGPCapability_ASN{asn})
|
|
|
|
if mplist != nil && len(mplist) > 0 {
|
|
for i := range mplist {
|
|
ptr.BGPCapabilities = append(ptr.BGPCapabilities, &mplist[i])
|
|
}
|
|
}
|
|
|
|
if addpathlist != nil && len(addpathlist) > 0 {
|
|
addpathcapa := BGPCapability_ADDPATH{AddPathList: addpathlist}
|
|
ptr.BGPCapabilities = append(ptr.BGPCapabilities, addpathcapa)
|
|
}
|
|
|
|
if routerefresh {
|
|
ptr.BGPCapabilities = append(ptr.BGPCapabilities, BGPCapability_ROUTEREFRESH{})
|
|
}
|
|
|
|
parameter := BGPParameter{
|
|
Type: PARAMETER_CAPA,
|
|
Data: ptr,
|
|
}
|
|
|
|
open.Parameters[0] = parameter
|
|
return open
|
|
}
|