├── doc.go ├── util.go ├── .travis.yml ├── packet_keepalive_test.go ├── packet_keepalive.go ├── neighbor.go ├── README.md ├── packet_notification_test.go ├── event_test.go ├── examples_test.go ├── collector_test.go ├── packet_test.go ├── packet_notification.go ├── packet.go ├── collector.go ├── event.go ├── packet_open_test.go ├── fsm_test.go ├── LICENSE ├── packet_open.go ├── fsm.go └── packet_update_test.go /doc.go: -------------------------------------------------------------------------------- 1 | // Package bgpls is a bgp link-state collector library for Go 2 | package bgpls 3 | -------------------------------------------------------------------------------- /util.go: -------------------------------------------------------------------------------- 1 | package bgpls 2 | 3 | func reverseByteOrder(b []byte) []byte { 4 | for i, j := 0, len(b)-1; i < j; i, j = i+1, j-1 { 5 | b[i], b[j] = b[j], b[i] 6 | } 7 | 8 | return b 9 | } 10 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: go 2 | go: 3 | - 1.9 4 | - "1.10" 5 | 6 | before_install: 7 | - go get golang.org/x/tools/cmd/cover 8 | - go get github.com/mattn/goveralls 9 | 10 | script: 11 | - go test -race -v -coverprofile=coverage.txt -covermode=atomic 12 | 13 | after_success: 14 | - bash <(curl -s https://codecov.io/bash) 15 | -------------------------------------------------------------------------------- /packet_keepalive_test.go: -------------------------------------------------------------------------------- 1 | package bgpls 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stretchr/testify/assert" 7 | ) 8 | 9 | func TestKeepaliveMessage(t *testing.T) { 10 | k := &keepAliveMessage{} 11 | 12 | b, err := k.serialize() 13 | if err != nil { 14 | t.Error(err) 15 | } 16 | 17 | m, err := messagesFromBytes(b) 18 | if err != nil { 19 | t.Error(err) 20 | } 21 | 22 | if len(m) != 1 { 23 | t.Fatalf("invalid number of messages deserialized: %d", len(m)) 24 | } 25 | 26 | f, ok := m[0].(*keepAliveMessage) 27 | if !ok { 28 | t.Error("not a keep alive message") 29 | } 30 | 31 | assert.Equal(t, f.MessageType(), KeepAliveMessageType) 32 | } 33 | -------------------------------------------------------------------------------- /packet_keepalive.go: -------------------------------------------------------------------------------- 1 | package bgpls 2 | 3 | import "errors" 4 | 5 | type keepAliveMessage struct{} 6 | 7 | func (k *keepAliveMessage) MessageType() MessageType { 8 | return KeepAliveMessageType 9 | } 10 | 11 | func (k *keepAliveMessage) serialize() ([]byte, error) { 12 | buff := prependHeader(make([]byte, 0), KeepAliveMessageType) 13 | return buff, nil 14 | } 15 | 16 | func (k *keepAliveMessage) deserialize(b []byte) error { 17 | if len(b) > 0 { 18 | return &errWithNotification{ 19 | error: errors.New("keep alive message invalid length"), 20 | code: NotifErrCodeMessageHeader, 21 | subcode: NotifErrSubcodeBadLength, 22 | } 23 | } 24 | return nil 25 | } 26 | -------------------------------------------------------------------------------- /neighbor.go: -------------------------------------------------------------------------------- 1 | package bgpls 2 | 3 | import ( 4 | "net" 5 | "time" 6 | ) 7 | 8 | // NeighborConfig is the configuration for a BGP-LS neighbor. 9 | type NeighborConfig struct { 10 | Address net.IP 11 | ASN uint32 12 | HoldTime time.Duration 13 | } 14 | 15 | type neighbor interface { 16 | fsm 17 | config() *NeighborConfig 18 | } 19 | 20 | type standardNeighbor struct { 21 | fsm 22 | c *NeighborConfig 23 | } 24 | 25 | func newNeighbor(routerID net.IP, localASN uint32, config *NeighborConfig, events chan Event) neighbor { 26 | n := &standardNeighbor{ 27 | c: config, 28 | } 29 | 30 | n.fsm = newFSM(n.config(), events, routerID, localASN, 179) 31 | 32 | return n 33 | } 34 | 35 | func (n *standardNeighbor) config() *NeighborConfig { 36 | return n.c 37 | } 38 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # This repository has been deprecated in favor of [corebgp](http://github.com/jwhited/corebgp). Please consider using it instead. 2 | 3 | # bgpls [![Build Status](https://travis-ci.org/jwhited/bgpls.svg?branch=master)](https://travis-ci.org/jwhited/bgpls) [![GoDoc](https://godoc.org/github.com/jwhited/bgpls?status.svg)](https://godoc.org/github.com/jwhited/bgpls) [![codecov](https://codecov.io/gh/jwhited/bgpls/branch/master/graph/badge.svg)](https://codecov.io/gh/jwhited/bgpls) 4 | A BGP Link-State collector library for Go 1.9+. 5 | 6 | ## Supported RFCs 7 | * [rfc7752](https://tools.ietf.org/html/rfc7752) 8 | * [draft-ietf-idr-bgp-ls-segment-routing-ext](https://tools.ietf.org/html/draft-ietf-idr-bgp-ls-segment-routing-ext) 9 | * [draft-ietf-idr-bgpls-segment-routing-epe](https://tools.ietf.org/html/draft-ietf-idr-bgpls-segment-routing-epe) 10 | * [draft-ietf-idr-te-pm-bgp](https://tools.ietf.org/html/draft-ietf-idr-te-pm-bgp) 11 | 12 | ## Usage 13 | [Collector example](https://godoc.org/github.com/jwhited/bgpls/#example-Collector) 14 | -------------------------------------------------------------------------------- /packet_notification_test.go: -------------------------------------------------------------------------------- 1 | package bgpls 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stretchr/testify/assert" 7 | ) 8 | 9 | func TestNotificationMessage(t *testing.T) { 10 | code := NotifErrCodeUpdateMessage 11 | subcode := NotifErrSubcodeMalformedAttr 12 | data := []byte{1, 1, 1, 1} 13 | n := &NotificationMessage{ 14 | Code: code, 15 | Subcode: subcode, 16 | Data: data, 17 | } 18 | 19 | b, err := n.serialize() 20 | if err != nil { 21 | t.Error(err) 22 | } 23 | 24 | m, err := messagesFromBytes(b) 25 | if err != nil { 26 | t.Error(err) 27 | } 28 | 29 | if len(m) != 1 { 30 | t.Errorf("invalid number of messages deserialized: %d", len(m)) 31 | } 32 | 33 | f, ok := m[0].(*NotificationMessage) 34 | if !ok { 35 | t.Error("not a notification message") 36 | } 37 | 38 | assert.Equal(t, f.MessageType(), NotificationMessageType) 39 | assert.Equal(t, code, f.Code) 40 | assert.Equal(t, subcode, f.Subcode) 41 | assert.Equal(t, len(data), len(f.Data)) 42 | 43 | for i, d := range data { 44 | assert.Equal(t, d, f.Data[i]) 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /event_test.go: -------------------------------------------------------------------------------- 1 | package bgpls 2 | 3 | import ( 4 | "errors" 5 | "net" 6 | "testing" 7 | "time" 8 | 9 | "github.com/stretchr/testify/assert" 10 | ) 11 | 12 | func TestEvent(t *testing.T) { 13 | conf := &NeighborConfig{ 14 | ASN: 64512, 15 | HoldTime: time.Second * 30, 16 | Address: net.ParseIP("172.16.0.1").To4(), 17 | } 18 | 19 | cases := []struct { 20 | event Event 21 | t EventType 22 | s string 23 | }{ 24 | {newEventNeighborErr(conf, errors.New("test")), EventTypeNeighborErr, "neighbor error"}, 25 | {newEventNeighborHoldTimerExpired(conf), EventTypeNeighborHoldTimerExpired, "neighbor hold timer expired"}, 26 | {newEventNeighborNotificationReceived(conf, &NotificationMessage{}), EventTypeNeighborNotificationReceived, "received notification message from neighbor"}, 27 | {newEventNeighborStateTransition(conf, IdleState), EventTypeNeighborStateTransition, "neighbor state changed"}, 28 | {newEventNeighborUpdateReceived(conf, &UpdateMessage{}), EventTypeNeighborUpdateReceived, "received update message from neighbor"}, 29 | } 30 | 31 | for _, c := range cases { 32 | assert.Equal(t, c.event.Type(), c.t) 33 | assert.Equal(t, c.event.Type().String(), c.s) 34 | _ = c.event.Neighbor() 35 | _ = c.event.Timestamp() 36 | } 37 | 38 | u := EventType(0) 39 | assert.Equal(t, u.String(), "unknown event type") 40 | } 41 | -------------------------------------------------------------------------------- /examples_test.go: -------------------------------------------------------------------------------- 1 | package bgpls_test 2 | 3 | import ( 4 | "log" 5 | "net" 6 | "time" 7 | 8 | "github.com/jwhited/bgpls" 9 | ) 10 | 11 | func ExampleCollector() { 12 | collectorConfig := &bgpls.CollectorConfig{ 13 | ASN: uint32(64512), 14 | RouterID: net.ParseIP("1.2.3.4"), 15 | EventBufferSize: 1024, 16 | } 17 | 18 | collector, err := bgpls.NewCollector(collectorConfig) 19 | if err != nil { 20 | log.Fatal(err) 21 | } 22 | 23 | neighborConfig := &bgpls.NeighborConfig{ 24 | Address: net.ParseIP("172.16.1.201"), 25 | ASN: uint32(64512), 26 | HoldTime: time.Second * 30, 27 | } 28 | 29 | err = collector.AddNeighbor(neighborConfig) 30 | if err != nil { 31 | log.Fatal(err) 32 | } 33 | 34 | eventsChan, err := collector.Events() 35 | if err != nil { 36 | log.Fatal(err) 37 | } 38 | 39 | for { 40 | event := <-eventsChan 41 | // all Event types can be found in event.go (EventNeighbor**) 42 | switch e := event.(type) { 43 | case *bgpls.EventNeighborErr: 44 | log.Printf("neighbor %s, err: %v", e.Neighbor().Address, e.Err) 45 | case *bgpls.EventNeighborStateTransition: 46 | log.Printf("neighbor %s, state transition: %v", e.Neighbor().Address, e.State) 47 | case *bgpls.EventNeighborNotificationReceived: 48 | log.Printf("neighbor %s notification message code: %v", e.Neighbor().Address, e.Message.Code) 49 | case *bgpls.EventNeighborUpdateReceived: 50 | log.Printf("neighbor %s update message", e.Neighbor().Address) 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /collector_test.go: -------------------------------------------------------------------------------- 1 | package bgpls 2 | 3 | import ( 4 | "net" 5 | "testing" 6 | "time" 7 | 8 | "github.com/stretchr/testify/assert" 9 | ) 10 | 11 | func TestCollector(t *testing.T) { 12 | collectorConfig := &CollectorConfig{ 13 | ASN: 1234, 14 | RouterID: net.ParseIP("172.16.1.106"), 15 | EventBufferSize: 1024, 16 | } 17 | 18 | c, err := NewCollector(collectorConfig) 19 | if err != nil { 20 | t.Fatal(err) 21 | } 22 | 23 | d := c.Config() 24 | assert.Equal(t, d, collectorConfig) 25 | 26 | neighborConfig := &NeighborConfig{ 27 | Address: net.ParseIP("127.0.0.1"), 28 | ASN: 1234, 29 | HoldTime: time.Second * 30, 30 | } 31 | 32 | err = c.AddNeighbor(neighborConfig) 33 | if err != nil { 34 | t.Fatal(err) 35 | } 36 | 37 | err = c.AddNeighbor(neighborConfig) 38 | assert.NotNil(t, err) 39 | 40 | _, err = c.Events() 41 | if err != nil { 42 | t.Fatal(err) 43 | } 44 | 45 | neighbors, err := c.Neighbors() 46 | if err != nil { 47 | t.Fatal(err) 48 | } 49 | assert.Len(t, neighbors, 1) 50 | assert.Equal(t, neighbors[0], neighborConfig) 51 | 52 | err = c.DeleteNeighbor(net.ParseIP("127.0.0.1")) 53 | if err != nil { 54 | t.Fatal(err) 55 | } 56 | 57 | err = c.DeleteNeighbor(net.ParseIP("127.0.0.2")) 58 | assert.NotNil(t, err) 59 | 60 | c.Stop() 61 | 62 | _, err = c.Events() 63 | assert.Equal(t, err, ErrCollectorStopped) 64 | 65 | err = c.AddNeighbor(neighborConfig) 66 | assert.Equal(t, err, ErrCollectorStopped) 67 | 68 | _, err = c.Neighbors() 69 | assert.Equal(t, err, ErrCollectorStopped) 70 | 71 | err = c.DeleteNeighbor(net.ParseIP("127.0.0.2")) 72 | assert.Equal(t, err, ErrCollectorStopped) 73 | 74 | c, err = NewCollector(collectorConfig) 75 | if err != nil { 76 | t.Fatal(err) 77 | } 78 | err = c.AddNeighbor(neighborConfig) 79 | if err != nil { 80 | t.Fatal(err) 81 | } 82 | c.Stop() 83 | c.Stop() 84 | } 85 | -------------------------------------------------------------------------------- /packet_test.go: -------------------------------------------------------------------------------- 1 | package bgpls 2 | 3 | import ( 4 | "encoding/binary" 5 | "testing" 6 | 7 | "github.com/stretchr/testify/assert" 8 | ) 9 | 10 | func TestMessageTypeString(t *testing.T) { 11 | assert.Equal(t, OpenMessageType.String(), "open") 12 | assert.Equal(t, UpdateMessageType.String(), "update") 13 | assert.Equal(t, NotificationMessageType.String(), "notification") 14 | assert.Equal(t, KeepAliveMessageType.String(), "keepalive") 15 | } 16 | 17 | func TestMessageFromBytes(t *testing.T) { 18 | k := &keepAliveMessage{} 19 | b, err := k.serialize() 20 | if err != nil { 21 | t.Fatal(err) 22 | } 23 | 24 | // invalid message header length 25 | binary.BigEndian.PutUint16(b[16:18], 0) 26 | _, err = messagesFromBytes(b) 27 | assert.NotNil(t, err) 28 | 29 | // error on keepalive deserialization 30 | b = append(b, 0) 31 | binary.BigEndian.PutUint16(b[16:18], 20) 32 | _, err = messagesFromBytes(b) 33 | assert.NotNil(t, err) 34 | 35 | // invalid marker 36 | b[15] = 0 37 | _, err = messagesFromBytes(b) 38 | assert.NotNil(t, err) 39 | 40 | // message < 19 bytes 41 | b = b[:18] 42 | _, err = messagesFromBytes(b) 43 | assert.NotNil(t, err) 44 | 45 | // error on open message deserialization 46 | o := &openMessage{} 47 | b, err = o.serialize() 48 | if err != nil { 49 | t.Fatal(err) 50 | } 51 | _, err = messagesFromBytes(b) 52 | assert.NotNil(t, err) 53 | 54 | // error on update message deserialization 55 | u := &UpdateMessage{} 56 | b, err = u.serialize() 57 | if err != nil { 58 | t.Fatal(err) 59 | } 60 | _, err = messagesFromBytes(b) 61 | assert.NotNil(t, err) 62 | 63 | // error on notification message deserialization 64 | n := &NotificationMessage{} 65 | b, err = n.serialize() 66 | if err != nil { 67 | t.Fatal(err) 68 | } 69 | b = b[:len(b)-2] 70 | binary.BigEndian.PutUint16(b[16:18], 19) 71 | _, err = messagesFromBytes(b) 72 | assert.NotNil(t, err) 73 | 74 | // invalid message type 75 | b[18] = 5 76 | _, err = messagesFromBytes(b) 77 | assert.NotNil(t, err) 78 | 79 | // 2 messages 80 | k = &keepAliveMessage{} 81 | b, err = k.serialize() 82 | if err != nil { 83 | t.Fatal(err) 84 | } 85 | b = append(b, b...) 86 | m, err := messagesFromBytes(b) 87 | assert.Len(t, m, 2) 88 | assert.Nil(t, err) 89 | } 90 | -------------------------------------------------------------------------------- /packet_notification.go: -------------------------------------------------------------------------------- 1 | package bgpls 2 | 3 | import "errors" 4 | 5 | // NotifErrCode is a notifcation message error code. 6 | type NotifErrCode uint8 7 | 8 | // NotifErrCode values 9 | const ( 10 | _ NotifErrCode = iota 11 | NotifErrCodeMessageHeader 12 | NotifErrCodeOpenMessage 13 | NotifErrCodeUpdateMessage 14 | NotifErrCodeHoldTimerExpired 15 | NotifErrCodeFsmError 16 | NotifErrCodeCease 17 | ) 18 | 19 | // NotifErrSubcode is a notification message error subcode. 20 | type NotifErrSubcode uint8 21 | 22 | // message header subcodes 23 | const ( 24 | _ NotifErrSubcode = iota 25 | NotifErrSubcodeConnNotSynch 26 | NotifErrSubcodeBadLength 27 | NotifErrSubcodeBadType 28 | ) 29 | 30 | // open message subcodes 31 | const ( 32 | _ NotifErrSubcode = iota 33 | NotifErrSubcodeUnsupportedVersionNumber 34 | NotifErrSubcodeBadPeerAS 35 | NotifErrSubcodeBadBgpID 36 | NotifErrSubcodeUnsupportedOptParam 37 | _ 38 | NotifErrSubcodeUnacceptableHoldTime 39 | NotifErrSubcodeUnsupportedCapability 40 | ) 41 | 42 | // update message subcodes 43 | const ( 44 | _ NotifErrSubcode = iota 45 | NotifErrSubcodeMalformedAttr 46 | NotifErrSubcodeUnrecognizedWellKnownAttr 47 | NotifErrSubcodeMissingWellKnownAttr 48 | NotifErrSubcodeAttrFlagsError 49 | NotifErrSubcodeAttrLenError 50 | NotifErrSubcodeInvalidOrigin 51 | _ 52 | NotifErrSubcodeInvalidNextHop 53 | NotifErrSubcodeOptionalAttrError 54 | NotifErrSubcodeInvalidNetworkField 55 | NotifErrSubcodeMalformedAsPath 56 | ) 57 | 58 | // NotificationMessage is a bgp message. 59 | // 60 | // https://tools.ietf.org/html/rfc4271#section-4.5 61 | type NotificationMessage struct { 62 | Code NotifErrCode 63 | Subcode NotifErrSubcode 64 | Data []byte 65 | } 66 | 67 | // MessageType returns the appropriate MessageType for NotificationMessage. 68 | func (n *NotificationMessage) MessageType() MessageType { 69 | return NotificationMessageType 70 | } 71 | 72 | func (n *NotificationMessage) serialize() ([]byte, error) { 73 | buff := make([]byte, 2) 74 | buff[0] = uint8(n.Code) 75 | buff[1] = uint8(n.Subcode) 76 | if len(n.Data) > 0 { 77 | buff = append(buff, n.Data...) 78 | } 79 | 80 | buff = prependHeader(buff, NotificationMessageType) 81 | 82 | return buff, nil 83 | } 84 | 85 | func (n *NotificationMessage) deserialize(b []byte) error { 86 | if len(b) < 2 { 87 | return errors.New("incomplete notification message") 88 | } 89 | 90 | n.Code = NotifErrCode(b[0]) 91 | n.Subcode = NotifErrSubcode(b[1]) 92 | 93 | if len(b) > 2 { 94 | n.Data = b[2:] 95 | } 96 | 97 | return nil 98 | } 99 | -------------------------------------------------------------------------------- /packet.go: -------------------------------------------------------------------------------- 1 | package bgpls 2 | 3 | import ( 4 | "encoding/binary" 5 | "errors" 6 | "fmt" 7 | ) 8 | 9 | // MessageType describes the type of bgp message. 10 | type MessageType uint8 11 | 12 | // MessageType values 13 | const ( 14 | OpenMessageType MessageType = 1 15 | UpdateMessageType MessageType = 2 16 | NotificationMessageType MessageType = 3 17 | KeepAliveMessageType MessageType = 4 18 | ) 19 | 20 | func (t MessageType) String() string { 21 | switch t { 22 | case OpenMessageType: 23 | return "open" 24 | case UpdateMessageType: 25 | return "update" 26 | case NotificationMessageType: 27 | return "notification" 28 | case KeepAliveMessageType: 29 | return "keepalive" 30 | default: 31 | return "unknown" 32 | } 33 | } 34 | 35 | // Message is a bgp message. 36 | type Message interface { 37 | MessageType() MessageType 38 | serialize() ([]byte, error) 39 | deserialize(b []byte) error 40 | } 41 | 42 | type errWithNotification struct { 43 | error 44 | code NotifErrCode 45 | subcode NotifErrSubcode 46 | data []byte 47 | } 48 | 49 | func messagesFromBytes(b []byte) ([]Message, error) { 50 | messages := make([]Message, 0) 51 | 52 | for { 53 | if len(b) < 19 { 54 | return nil, &errWithNotification{ 55 | error: errors.New("message < 19 bytes"), 56 | code: NotifErrCodeMessageHeader, 57 | subcode: NotifErrSubcodeBadLength, 58 | } 59 | } 60 | 61 | for i := 0; i < 16; i++ { 62 | if b[i] != 0xFF { 63 | return nil, &errWithNotification{ 64 | error: errors.New("invalid message header marker value"), 65 | code: NotifErrCodeMessageHeader, 66 | subcode: NotifErrSubcodeConnNotSynch, 67 | } 68 | } 69 | } 70 | 71 | msgLen := binary.BigEndian.Uint16(b[16:18]) 72 | if len(b) < int(msgLen) || msgLen < 19 { 73 | return nil, &errWithNotification{ 74 | error: errors.New("message header length invalid"), 75 | code: NotifErrCodeMessageHeader, 76 | subcode: NotifErrSubcodeBadLength, 77 | } 78 | } 79 | 80 | msgType := MessageType(b[18]) 81 | msgBytes := b[19:msgLen] 82 | 83 | switch msgType { 84 | case OpenMessageType: 85 | m := &openMessage{} 86 | err := m.deserialize(msgBytes) 87 | if err != nil { 88 | return nil, err 89 | } 90 | messages = append(messages, m) 91 | case KeepAliveMessageType: 92 | m := &keepAliveMessage{} 93 | err := m.deserialize(msgBytes) 94 | if err != nil { 95 | return nil, err 96 | } 97 | messages = append(messages, m) 98 | case UpdateMessageType: 99 | m := &UpdateMessage{} 100 | err := m.deserialize(msgBytes) 101 | if err != nil { 102 | return nil, err 103 | } 104 | messages = append(messages, m) 105 | case NotificationMessageType: 106 | m := &NotificationMessage{} 107 | err := m.deserialize(msgBytes) 108 | if err != nil { 109 | return nil, err 110 | } 111 | messages = append(messages, m) 112 | default: 113 | return nil, &errWithNotification{ 114 | error: fmt.Errorf("invalid message type %s", msgType), 115 | code: NotifErrCodeMessageHeader, 116 | subcode: NotifErrSubcodeBadType, 117 | } 118 | } 119 | 120 | if len(b) > int(msgLen) { 121 | b = b[msgLen:] 122 | } else { 123 | break 124 | } 125 | } 126 | 127 | return messages, nil 128 | } 129 | 130 | func prependHeader(b []byte, t MessageType) []byte { 131 | buff := make([]byte, 19, 512) 132 | 133 | // marker 134 | for i := 0; i < 16; i++ { 135 | buff[i] = 0xFF 136 | } 137 | 138 | // length 139 | binary.BigEndian.PutUint16(buff[16:18], uint16(len(b)+19)) 140 | 141 | // type 142 | buff[18] = uint8(t) 143 | 144 | // value 145 | buff = append(buff, b...) 146 | return buff 147 | } 148 | -------------------------------------------------------------------------------- /collector.go: -------------------------------------------------------------------------------- 1 | package bgpls 2 | 3 | import ( 4 | "errors" 5 | "net" 6 | "sync" 7 | ) 8 | 9 | // ErrCollectorStopped is returned when an operation is not valid due to the collector being stopped 10 | var ErrCollectorStopped = errors.New("collector is stopped") 11 | 12 | // Collector is a BGP Link-State collector 13 | // 14 | // Events() returns the events channel or an error if the collector has been stopped. 15 | // The events channel is buffered, its size is configurable via CollectorConfig. 16 | // The events channel should be read from in a loop. It will close only when Stop() is called. 17 | // 18 | // Config() returns the configuration of the Collector. 19 | // 20 | // AddNeighbor() initializes a new bgp-ls neighbor. 21 | // An error is returned if the collector is stopped or the neighbor already exists. 22 | // 23 | // DeleteNeighbor() shuts down and removes a neighbor from the collector. 24 | // An error is returned if the collector is stopped or the neighbor does not exist. 25 | // 26 | // Neighbors() returns the configuration of all neighbors. 27 | // 28 | // Stop() stops the collector and all neighbors. 29 | type Collector interface { 30 | Events() (<-chan Event, error) 31 | Config() *CollectorConfig 32 | AddNeighbor(c *NeighborConfig) error 33 | DeleteNeighbor(address net.IP) error 34 | Neighbors() ([]*NeighborConfig, error) 35 | Stop() 36 | } 37 | 38 | // standardCollector satisifies the Collector interface. 39 | type standardCollector struct { 40 | running bool 41 | events chan Event 42 | config *CollectorConfig 43 | neighbors map[string]neighbor 44 | *sync.RWMutex 45 | } 46 | 47 | // CollectorConfig is the configuration for the Collector. 48 | // EventBufferSize is the size of the buffered events channel returned from the Events() Collector method. 49 | // It should be set to a value appropriate from a memory consumption perspective. 50 | // Setting this value too low can inhibit bgp io. 51 | type CollectorConfig struct { 52 | ASN uint32 53 | RouterID net.IP 54 | EventBufferSize uint64 55 | } 56 | 57 | // NewCollector creates a Collector. 58 | func NewCollector(config *CollectorConfig) (Collector, error) { 59 | c := &standardCollector{ 60 | running: true, 61 | events: make(chan Event, config.EventBufferSize), 62 | config: config, 63 | neighbors: make(map[string]neighbor), 64 | RWMutex: &sync.RWMutex{}, 65 | } 66 | 67 | return c, nil 68 | } 69 | 70 | func (c *standardCollector) Events() (<-chan Event, error) { 71 | c.RLock() 72 | defer c.RUnlock() 73 | if c.running { 74 | return c.events, nil 75 | } 76 | return nil, ErrCollectorStopped 77 | } 78 | 79 | func (c *standardCollector) Config() *CollectorConfig { 80 | c.RLock() 81 | defer c.RUnlock() 82 | return c.config 83 | } 84 | 85 | func (c *standardCollector) AddNeighbor(config *NeighborConfig) error { 86 | c.Lock() 87 | defer c.Unlock() 88 | 89 | if !c.running { 90 | return ErrCollectorStopped 91 | } 92 | 93 | _, exists := c.neighbors[config.Address.String()] 94 | if exists { 95 | return errors.New("neighbor exists") 96 | } 97 | 98 | n := newNeighbor(c.config.RouterID, c.config.ASN, config, c.events) 99 | c.neighbors[config.Address.String()] = n 100 | 101 | return nil 102 | } 103 | 104 | func (c *standardCollector) Neighbors() ([]*NeighborConfig, error) { 105 | c.RLock() 106 | defer c.RUnlock() 107 | 108 | if !c.running { 109 | return nil, ErrCollectorStopped 110 | } 111 | 112 | configs := make([]*NeighborConfig, 0) 113 | for _, n := range c.neighbors { 114 | configs = append(configs, n.config()) 115 | } 116 | 117 | return configs, nil 118 | } 119 | 120 | func (c *standardCollector) DeleteNeighbor(address net.IP) error { 121 | c.Lock() 122 | defer c.Unlock() 123 | 124 | if !c.running { 125 | return ErrCollectorStopped 126 | } 127 | 128 | n, exists := c.neighbors[address.String()] 129 | if !exists { 130 | return errors.New("neighbor does not exist") 131 | } 132 | 133 | n.terminate() 134 | delete(c.neighbors, address.String()) 135 | 136 | return nil 137 | } 138 | 139 | func (c *standardCollector) Stop() { 140 | c.Lock() 141 | defer c.Unlock() 142 | 143 | if !c.running { 144 | return 145 | } 146 | 147 | wg := &sync.WaitGroup{} 148 | for _, n := range c.neighbors { 149 | wg.Add(1) 150 | n := n 151 | go func() { 152 | n.terminate() 153 | wg.Done() 154 | }() 155 | } 156 | wg.Wait() 157 | 158 | c.running = false 159 | close(c.events) 160 | } 161 | -------------------------------------------------------------------------------- /event.go: -------------------------------------------------------------------------------- 1 | package bgpls 2 | 3 | import "time" 4 | 5 | // Event is a Collector event associated with a neighbor. 6 | // 7 | // Neighbor() returns the associated neighbor's configuration. 8 | // 9 | // Timestamp() returns the time at which the event occurred. 10 | // 11 | // Type() returns the event type. 12 | type Event interface { 13 | Neighbor() *NeighborConfig 14 | Timestamp() time.Time 15 | Type() EventType 16 | } 17 | 18 | // EventType describes the type of event 19 | type EventType int 20 | 21 | // EventType values 22 | const ( 23 | _ EventType = iota 24 | EventTypeNeighborErr 25 | EventTypeNeighborHoldTimerExpired 26 | EventTypeNeighborStateTransition 27 | EventTypeNeighborUpdateReceived 28 | EventTypeNeighborNotificationReceived 29 | ) 30 | 31 | func (e EventType) String() string { 32 | switch e { 33 | case EventTypeNeighborErr: 34 | return "neighbor error" 35 | case EventTypeNeighborHoldTimerExpired: 36 | return "neighbor hold timer expired" 37 | case EventTypeNeighborStateTransition: 38 | return "neighbor state changed" 39 | case EventTypeNeighborUpdateReceived: 40 | return "received update message from neighbor" 41 | case EventTypeNeighborNotificationReceived: 42 | return "received notification message from neighbor" 43 | default: 44 | return "unknown event type" 45 | } 46 | } 47 | 48 | // BaseEvent is included in every Event 49 | type BaseEvent struct { 50 | t time.Time 51 | n *NeighborConfig 52 | } 53 | 54 | // Timestamp returns the time at which the event occurred 55 | func (b *BaseEvent) Timestamp() time.Time { 56 | return b.t 57 | } 58 | 59 | // Neighbor returns the NeighborConfig associated with the event 60 | func (b *BaseEvent) Neighbor() *NeighborConfig { 61 | return b.n 62 | } 63 | 64 | // EventNeighborErr is generated when a neighbor encounters an error 65 | type EventNeighborErr struct { 66 | BaseEvent 67 | Err error 68 | } 69 | 70 | // Type returns the appropriate EventType for EventNeighborErr 71 | func (e *EventNeighborErr) Type() EventType { 72 | return EventTypeNeighborErr 73 | } 74 | 75 | func newEventNeighborErr(c *NeighborConfig, err error) Event { 76 | return &EventNeighborErr{ 77 | BaseEvent: BaseEvent{ 78 | t: time.Now(), 79 | n: c, 80 | }, 81 | Err: err, 82 | } 83 | } 84 | 85 | // EventNeighborHoldTimerExpired is generated when a neighbor's hold timer expires 86 | type EventNeighborHoldTimerExpired struct { 87 | BaseEvent 88 | } 89 | 90 | // Type returns the appropriate EventType for EventNeighborHoldTimerExpired 91 | func (e *EventNeighborHoldTimerExpired) Type() EventType { 92 | return EventTypeNeighborHoldTimerExpired 93 | } 94 | 95 | func newEventNeighborHoldTimerExpired(c *NeighborConfig) Event { 96 | return &EventNeighborHoldTimerExpired{ 97 | BaseEvent: BaseEvent{ 98 | t: time.Now(), 99 | n: c, 100 | }, 101 | } 102 | } 103 | 104 | // EventNeighborStateTransition is generated when a neighbor's fsm transitions to a new state 105 | type EventNeighborStateTransition struct { 106 | BaseEvent 107 | State FSMState 108 | } 109 | 110 | // Type returns the appropriate EventType for EventNeighborStateTransition 111 | func (e *EventNeighborStateTransition) Type() EventType { 112 | return EventTypeNeighborStateTransition 113 | } 114 | 115 | func newEventNeighborStateTransition(c *NeighborConfig, s FSMState) Event { 116 | return &EventNeighborStateTransition{ 117 | BaseEvent: BaseEvent{ 118 | t: time.Now(), 119 | n: c, 120 | }, 121 | State: s, 122 | } 123 | } 124 | 125 | // EventNeighborUpdateReceived is generated when an update message is received 126 | type EventNeighborUpdateReceived struct { 127 | BaseEvent 128 | Message *UpdateMessage 129 | } 130 | 131 | // Type returns the appropriate EventType for EventNeighborUpdateReceived 132 | func (e *EventNeighborUpdateReceived) Type() EventType { 133 | return EventTypeNeighborUpdateReceived 134 | } 135 | 136 | func newEventNeighborUpdateReceived(c *NeighborConfig, u *UpdateMessage) Event { 137 | return &EventNeighborUpdateReceived{ 138 | BaseEvent: BaseEvent{ 139 | t: time.Now(), 140 | n: c, 141 | }, 142 | Message: u, 143 | } 144 | } 145 | 146 | // EventNeighborNotificationReceived is generated when a notification message is received 147 | type EventNeighborNotificationReceived struct { 148 | BaseEvent 149 | Message *NotificationMessage 150 | } 151 | 152 | // Type returns the appropriate EventType for EventNeighborNotificationReceived 153 | func (e *EventNeighborNotificationReceived) Type() EventType { 154 | return EventTypeNeighborNotificationReceived 155 | } 156 | 157 | func newEventNeighborNotificationReceived(c *NeighborConfig, n *NotificationMessage) Event { 158 | return &EventNeighborNotificationReceived{ 159 | BaseEvent: BaseEvent{ 160 | t: time.Now(), 161 | n: c, 162 | }, 163 | Message: n, 164 | } 165 | } 166 | -------------------------------------------------------------------------------- /packet_open_test.go: -------------------------------------------------------------------------------- 1 | package bgpls 2 | 3 | import ( 4 | "encoding/binary" 5 | "math" 6 | "net" 7 | "testing" 8 | "time" 9 | 10 | "github.com/stretchr/testify/assert" 11 | ) 12 | 13 | type fakeOptParam struct{} 14 | 15 | func (f *fakeOptParam) optParamType() optParamType { 16 | return optParamType(0) 17 | } 18 | func (f *fakeOptParam) serialize() ([]byte, error) { 19 | return nil, nil 20 | } 21 | func (f *fakeOptParam) deserialize(b []byte) error { 22 | return nil 23 | } 24 | 25 | func TestDeserializeOptParams(t *testing.T) { 26 | c := &capabilityOptParam{ 27 | caps: []capability{ 28 | &capFourOctetAs{ 29 | asn: 1, 30 | }, 31 | }, 32 | } 33 | b, err := c.serialize() 34 | if err != nil { 35 | t.Fatal(err) 36 | } 37 | 38 | // error on cap deserialization 39 | b = b[:len(b)-1] 40 | b[1] = uint8(b[1] - 1) 41 | _, err = deserializeOptParams(b) 42 | assert.NotNil(t, err) 43 | 44 | // invalid param len 45 | b[1] = uint8(math.MaxUint8) 46 | _, err = deserializeOptParams(b) 47 | assert.NotNil(t, err) 48 | } 49 | 50 | func TestCapOptParam(t *testing.T) { 51 | c := &capabilityOptParam{} 52 | assert.Equal(t, c.optParamType(), capabilityOptParamType) 53 | 54 | // empty caps 55 | _, err := c.serialize() 56 | assert.NotNil(t, err) 57 | 58 | // error serializing cap 59 | 60 | // len < 2 deserializing 61 | err = c.deserialize([]byte{0}) 62 | assert.NotNil(t, err) 63 | 64 | // invalid param len 65 | err = c.deserialize([]byte{0, 2, 0}) 66 | assert.NotNil(t, err) 67 | 68 | // deserialize capUnknown 69 | u := &capUnknown{code: 100} 70 | b, err := u.serialize() 71 | if err != nil { 72 | t.Fatal(err) 73 | } 74 | 75 | err = c.deserialize(b) 76 | assert.Nil(t, err) 77 | } 78 | 79 | func TestCapUnknown(t *testing.T) { 80 | c := &capUnknown{} 81 | err := c.deserialize([]byte{0, 0, 0}) 82 | assert.Nil(t, err) 83 | _, err = c.serialize() 84 | assert.Nil(t, err) 85 | assert.Equal(t, c.capabilityCode(), capabilityCode(0)) 86 | } 87 | 88 | func TestCapMultiproto(t *testing.T) { 89 | c := &capMultiproto{} 90 | err := c.deserialize([]byte{0}) 91 | assert.NotNil(t, err) 92 | assert.Equal(t, c.capabilityCode(), capCodeMultiproto) 93 | } 94 | 95 | func TestCapFourOctetAs(t *testing.T) { 96 | c := &capFourOctetAs{} 97 | err := c.deserialize([]byte{0}) 98 | assert.NotNil(t, err) 99 | assert.Equal(t, c.capabilityCode(), capCodeFourOctetAs) 100 | } 101 | 102 | func TestValidateOpenMessage(t *testing.T) { 103 | // valid 104 | o, err := newOpenMessage(1, time.Second*3, net.ParseIP("172.16.1.1")) 105 | if err != nil { 106 | t.Fatal(err) 107 | } 108 | err = validateOpenMessage(o, 1) 109 | if err != nil { 110 | t.Fatal(err) 111 | } 112 | 113 | // asn mimatch 114 | err = validateOpenMessage(o, 2) 115 | assert.NotNil(t, err) 116 | 117 | // bad version 118 | o.version = 2 119 | err = validateOpenMessage(o, 1) 120 | assert.NotNil(t, err) 121 | 122 | // bad hold time 123 | o, err = newOpenMessage(1, time.Second*2, net.ParseIP("172.16.1.1")) 124 | if err != nil { 125 | t.Fatal(err) 126 | } 127 | err = validateOpenMessage(o, 1) 128 | assert.NotNil(t, err) 129 | 130 | // non-cap opt param 131 | o, err = newOpenMessage(1, time.Second*3, net.ParseIP("172.16.1.1")) 132 | if err != nil { 133 | t.Fatal(err) 134 | } 135 | o.optParams = []optParam{&fakeOptParam{}} 136 | err = validateOpenMessage(o, 1) 137 | assert.NotNil(t, err) 138 | 139 | // bad bgp id 140 | o, err = newOpenMessage(1, time.Second*3, []byte{0, 0, 0, 0}) 141 | if err != nil { 142 | t.Fatal(err) 143 | } 144 | err = validateOpenMessage(o, 1) 145 | assert.NotNil(t, err) 146 | 147 | // bad opt params 148 | o.holdTime = 3 149 | o.bgpID = 1 150 | o.optParams = nil 151 | err = validateOpenMessage(o, 1) 152 | assert.NotNil(t, err) 153 | 154 | // test 4 octet asn 155 | o, err = newOpenMessage(523456, time.Second*3, net.ParseIP("172.16.1.1")) 156 | if err != nil { 157 | t.Fatal(err) 158 | } 159 | err = validateOpenMessage(o, 523456) 160 | assert.Nil(t, err) 161 | 162 | // 4 octet indicated but not found in cap 163 | o, err = newOpenMessage(uint32(asTrans), time.Second*3, net.ParseIP("172.16.1.1")) 164 | if err != nil { 165 | t.Fatal(err) 166 | } 167 | o.optParams = []optParam{ 168 | &capabilityOptParam{ 169 | caps: []capability{ 170 | &capMultiproto{ 171 | afi: BgpLsAfi, 172 | safi: BgpLsSafi, 173 | }, 174 | }, 175 | }, 176 | } 177 | err = validateOpenMessage(o, 5) 178 | assert.NotNil(t, err) 179 | 180 | // bad peer asn in 4 octet cap 181 | o, err = newOpenMessage(uint32(asTrans), time.Second*3, net.ParseIP("172.16.1.1")) 182 | if err != nil { 183 | t.Fatal(err) 184 | } 185 | o.optParams = []optParam{ 186 | &capabilityOptParam{ 187 | caps: []capability{ 188 | &capFourOctetAs{ 189 | asn: 6, 190 | }, 191 | &capMultiproto{ 192 | afi: BgpLsAfi, 193 | safi: BgpLsSafi, 194 | }, 195 | }, 196 | }, 197 | } 198 | err = validateOpenMessage(o, 5) 199 | assert.NotNil(t, err) 200 | } 201 | 202 | func TestOpenMessage(t *testing.T) { 203 | asn := uint16(64512) 204 | holdTime := time.Second * 30 205 | bgpID := net.ParseIP("172.16.0.1") 206 | 207 | // invalid bgp id 208 | _, err := newOpenMessage(uint32(asn), holdTime, []byte{0}) 209 | assert.NotNil(t, err) 210 | 211 | // invalid opt params 212 | o := &openMessage{ 213 | optParams: []optParam{ 214 | &capabilityOptParam{}, 215 | }, 216 | } 217 | _, err = o.serialize() 218 | assert.NotNil(t, err) 219 | 220 | // invalid length 221 | err = o.deserialize([]byte{0}) 222 | assert.NotNil(t, err) 223 | 224 | // invald opt params len 225 | o, err = newOpenMessage(uint32(asn), holdTime, bgpID) 226 | if err != nil { 227 | t.Fatal(err) 228 | } 229 | b, err := o.serialize() 230 | if err != nil { 231 | t.Fatal(err) 232 | } 233 | b[9] = uint8(1) 234 | err = o.deserialize(b) 235 | assert.NotNil(t, err) 236 | 237 | o, err = newOpenMessage(uint32(asn), holdTime, bgpID) 238 | if err != nil { 239 | t.Fatal(err) 240 | } 241 | 242 | b, err = o.serialize() 243 | if err != nil { 244 | t.Fatal(err) 245 | } 246 | 247 | m, err := messagesFromBytes(b) 248 | if err != nil { 249 | t.Fatal(err) 250 | } 251 | 252 | if len(m) != 1 { 253 | t.Fatalf("invalid number of messages deserialized: %d", len(m)) 254 | } 255 | 256 | f, ok := m[0].(*openMessage) 257 | if !ok { 258 | t.Fatal("not an open message") 259 | } 260 | 261 | assert.Equal(t, asn, f.asn) 262 | assert.Equal(t, uint16(holdTime/time.Second), f.holdTime) 263 | assert.Equal(t, binary.BigEndian.Uint32(bgpID[12:16]), f.bgpID) 264 | assert.Equal(t, f.MessageType(), OpenMessageType) 265 | assert.Equal(t, len(o.optParams), len(f.optParams)) 266 | assert.Equal(t, f.version, uint8(4)) 267 | 268 | if len(f.optParams) != 1 { 269 | t.Fatal("missing optional param") 270 | } 271 | 272 | p, ok := f.optParams[0].(*capabilityOptParam) 273 | if !ok { 274 | t.Fatal("not capability optional param") 275 | } 276 | 277 | if len(p.caps) != 2 { 278 | t.Fatal("missing capabilities") 279 | } 280 | 281 | q, ok := p.caps[0].(*capFourOctetAs) 282 | if !ok { 283 | t.Fatal("missing four octet ASN capability") 284 | } 285 | assert.Equal(t, q.asn, uint32(asn)) 286 | 287 | r, ok := p.caps[1].(*capMultiproto) 288 | if !ok { 289 | t.Fatal("missing multiprotocol capability") 290 | } 291 | assert.Equal(t, r.capabilityCode(), capCodeMultiproto) 292 | assert.Equal(t, r.afi, BgpLsAfi) 293 | assert.Equal(t, r.safi, BgpLsSafi) 294 | } 295 | -------------------------------------------------------------------------------- /fsm_test.go: -------------------------------------------------------------------------------- 1 | package bgpls 2 | 3 | import ( 4 | "net" 5 | "strconv" 6 | "strings" 7 | "testing" 8 | "time" 9 | 10 | "github.com/stretchr/testify/assert" 11 | "github.com/stretchr/testify/suite" 12 | ) 13 | 14 | func TestFSMString(t *testing.T) { 15 | cases := []struct { 16 | state FSMState 17 | str string 18 | }{ 19 | {DisabledState, "disabled"}, 20 | {IdleState, "idle"}, 21 | {ConnectState, "connect"}, 22 | {ActiveState, "active"}, 23 | {OpenSentState, "openSent"}, 24 | {OpenConfirmState, "openConfirm"}, 25 | {EstablishedState, "established"}, 26 | {FSMState(10), "unknown state"}, 27 | } 28 | 29 | for _, c := range cases { 30 | assert.Equal(t, c.state.String(), c.str) 31 | } 32 | } 33 | 34 | type fsmTestSuite struct { 35 | suite.Suite 36 | neighborConfig *NeighborConfig 37 | ln net.Listener 38 | conn net.Conn 39 | events chan Event 40 | fsm fsm 41 | } 42 | 43 | func (s *fsmTestSuite) AfterTest(_, _ string) { 44 | s.fsm.terminate() 45 | s.conn.Close() 46 | s.ln.Close() 47 | } 48 | 49 | func (s *fsmTestSuite) readMessagesFromConn() ([]Message, error) { 50 | b := make([]byte, 4096) 51 | n, err := s.conn.Read(b) 52 | if err != nil { 53 | return nil, err 54 | } 55 | return messagesFromBytes(b[:n]) 56 | } 57 | 58 | func (s *fsmTestSuite) sendKeepalive() error { 59 | k := &keepAliveMessage{} 60 | b, err := k.serialize() 61 | if err != nil { 62 | return err 63 | } 64 | _, err = s.conn.Write(b) 65 | return err 66 | } 67 | 68 | func (s *fsmTestSuite) sendOpen() error { 69 | o, err := newOpenMessage(s.neighborConfig.ASN, s.neighborConfig.HoldTime, net.ParseIP("127.0.0.1")) 70 | if err != nil { 71 | return err 72 | } 73 | 74 | b, err := o.serialize() 75 | if err != nil { 76 | return err 77 | } 78 | 79 | _, err = s.conn.Write(b) 80 | return err 81 | } 82 | 83 | func (s *fsmTestSuite) advanceToOpenSentState() { 84 | ln, err := net.Listen("tcp", "127.0.0.1:0") 85 | if err != nil { 86 | assert.FailNow(s.T(), err.Error()) 87 | } 88 | 89 | s.ln = ln 90 | 91 | split := strings.Split(s.ln.Addr().String(), ":") 92 | if len(split) != 2 { 93 | assert.FailNow(s.T(), "unable to split listener address string") 94 | } 95 | 96 | i, err := strconv.Atoi(split[1]) 97 | if err != nil { 98 | assert.FailNow(s.T(), "unexpected split on listener address string") 99 | } 100 | 101 | s.neighborConfig = &NeighborConfig{ 102 | Address: net.ParseIP("127.0.0.1"), 103 | ASN: 64512, 104 | HoldTime: time.Second * 3, 105 | } 106 | 107 | s.events = make(chan Event) 108 | s.fsm = newFSM(s.neighborConfig, s.events, net.ParseIP("127.0.0.2").To4(), 64512, i) 109 | 110 | s.failNowIfNotStateTransition(IdleState) 111 | s.failNowIfNotStateTransition(ConnectState) 112 | 113 | conn, err := ln.Accept() 114 | if err != nil { 115 | assert.FailNow(s.T(), err.Error()) 116 | } 117 | 118 | s.failNowIfNotStateTransition(OpenSentState) 119 | 120 | s.conn = conn 121 | 122 | m, err := s.readMessagesFromConn() 123 | if err != nil { 124 | assert.FailNow(s.T(), err.Error()) 125 | } 126 | if !assert.Equal(s.T(), len(m), 1) { 127 | assert.FailNow(s.T(), "invalid number of messages") 128 | } 129 | } 130 | 131 | func (s *fsmTestSuite) advanceToOpenConfirmState() { 132 | s.advanceToOpenSentState() 133 | 134 | err := s.sendOpen() 135 | if err != nil { 136 | assert.FailNow(s.T(), err.Error()) 137 | } 138 | 139 | m, err := s.readMessagesFromConn() 140 | if err != nil { 141 | assert.FailNow(s.T(), err.Error()) 142 | } 143 | 144 | if assert.Len(s.T(), m, 1) { 145 | assert.IsType(s.T(), m[0], &keepAliveMessage{}) 146 | } 147 | 148 | s.failNowIfNotStateTransition(OpenConfirmState) 149 | } 150 | 151 | func (s *fsmTestSuite) failNowIfNotStateTransition(state FSMState) { 152 | e := <-s.events 153 | if assert.IsType(s.T(), &EventNeighborStateTransition{}, e) { 154 | f, _ := e.(*EventNeighborStateTransition) 155 | if !assert.Equal(s.T(), f.State, state) { 156 | assert.FailNow(s.T(), "unexpected state") 157 | } 158 | } else { 159 | assert.FailNow(s.T(), "unexpected event type") 160 | } 161 | } 162 | 163 | func (s *fsmTestSuite) advanceToEstablishedState() { 164 | s.advanceToOpenConfirmState() 165 | 166 | err := s.sendKeepalive() 167 | if err != nil { 168 | assert.FailNow(s.T(), err.Error()) 169 | } 170 | 171 | s.failNowIfNotStateTransition(EstablishedState) 172 | } 173 | 174 | func (s *fsmTestSuite) sendInvalidMsgExpectNeighborErr() { 175 | _, err := s.conn.Write([]byte{0}) 176 | if err != nil { 177 | assert.FailNow(s.T(), err.Error()) 178 | } 179 | 180 | m, err := s.readMessagesFromConn() 181 | if err != nil { 182 | assert.FailNow(s.T(), err.Error()) 183 | } 184 | if assert.Len(s.T(), m, 1) { 185 | assert.IsType(s.T(), &NotificationMessage{}, m[0]) 186 | } 187 | 188 | e := <-s.events 189 | assert.IsType(s.T(), &EventNeighborErr{}, e) 190 | } 191 | 192 | // advance to open sent state then cleanup 193 | func (s *fsmTestSuite) TestFSMOpenSentDisable() { 194 | s.advanceToOpenSentState() 195 | } 196 | 197 | // advance to open sent state then send an invalid message 198 | func (s *fsmTestSuite) TestFSMOpenSentReaderErr() { 199 | s.advanceToOpenSentState() 200 | s.sendInvalidMsgExpectNeighborErr() 201 | } 202 | 203 | // advance to open sent state and wait for hold timer to expire 204 | func (s *fsmTestSuite) TestFSMOpenSentHoldTimerExpired() { 205 | longHoldTime = time.Second * 1 206 | s.advanceToOpenSentState() 207 | e := <-s.events 208 | assert.IsType(s.T(), &EventNeighborHoldTimerExpired{}, e) 209 | } 210 | 211 | // advance to open sent state and send KA instead of open message 212 | func (s *fsmTestSuite) TestFSMOpenSentSendKA() { 213 | s.advanceToOpenSentState() 214 | err := s.sendKeepalive() 215 | if err != nil { 216 | assert.FailNow(s.T(), err.Error()) 217 | } 218 | e := <-s.events 219 | assert.IsType(s.T(), &EventNeighborErr{}, e) 220 | s.failNowIfNotStateTransition(IdleState) 221 | } 222 | 223 | // advance to open sent state and send notif 224 | func (s *fsmTestSuite) TestFSMOpenSentSendNotif() { 225 | s.advanceToOpenSentState() 226 | n := &NotificationMessage{Code: NotifErrCodeUpdateMessage, Subcode: NotifErrSubcodeMalformedAttr} 227 | b, err := n.serialize() 228 | if err != nil { 229 | assert.FailNow(s.T(), err.Error()) 230 | } 231 | _, err = s.conn.Write(b) 232 | if err != nil { 233 | assert.FailNow(s.T(), err.Error()) 234 | } 235 | e := <-s.events 236 | if assert.IsType(s.T(), &EventNeighborNotificationReceived{}, e) { 237 | f, _ := e.(*EventNeighborNotificationReceived) 238 | assert.Equal(s.T(), f.Message, n) 239 | } 240 | s.failNowIfNotStateTransition(IdleState) 241 | } 242 | 243 | // advance to open sent state and send an invalid open message 244 | func (s *fsmTestSuite) TestFSMOpenSentSendInvalidOpen() { 245 | s.advanceToOpenSentState() 246 | o, err := newOpenMessage(12, time.Second*3, []byte{0, 0, 0, 0}) 247 | if err != nil { 248 | assert.FailNow(s.T(), err.Error()) 249 | } 250 | b, err := o.serialize() 251 | if err != nil { 252 | assert.FailNow(s.T(), err.Error()) 253 | } 254 | _, err = s.conn.Write(b) 255 | if err != nil { 256 | assert.FailNow(s.T(), err.Error()) 257 | } 258 | m, err := s.readMessagesFromConn() 259 | assert.Nil(s.T(), err) 260 | if assert.Len(s.T(), m, 1) { 261 | assert.IsType(s.T(), m[0], &NotificationMessage{}) 262 | } 263 | e := <-s.events 264 | assert.IsType(s.T(), &EventNeighborErr{}, e) 265 | s.failNowIfNotStateTransition(IdleState) 266 | } 267 | 268 | // advance to open confirm state then cleanup 269 | func (s *fsmTestSuite) TestFSMOpenConfirmDisable() { 270 | s.advanceToOpenConfirmState() 271 | } 272 | 273 | // advance to open confirm state then send an invalid message 274 | func (s *fsmTestSuite) TestFSMOpenConfirmReaderErr() { 275 | s.advanceToOpenConfirmState() 276 | s.sendInvalidMsgExpectNeighborErr() 277 | s.failNowIfNotStateTransition(IdleState) 278 | } 279 | 280 | // advance to open confirm state and wait for hold timer to expire 281 | func (s *fsmTestSuite) TestFSMOpenConfirmHoldTimerExpire() { 282 | s.advanceToOpenConfirmState() 283 | e := <-s.events 284 | assert.IsType(s.T(), &EventNeighborHoldTimerExpired{}, e) 285 | s.failNowIfNotStateTransition(IdleState) 286 | } 287 | 288 | // advance to established state then cleanup 289 | func (s *fsmTestSuite) TestFSMEstablishedDisable() { 290 | s.advanceToEstablishedState() 291 | } 292 | 293 | // advance to established state then send an invalid message 294 | func (s *fsmTestSuite) TestFSMEstablishedReaderErr() { 295 | s.advanceToEstablishedState() 296 | s.sendInvalidMsgExpectNeighborErr() 297 | s.failNowIfNotStateTransition(IdleState) 298 | } 299 | 300 | // advance to established state and wait for hold timer to expire 301 | func (s *fsmTestSuite) TestFSMEstablishedHoldTimerExpired() { 302 | s.advanceToEstablishedState() 303 | e := <-s.events 304 | assert.IsType(s.T(), &EventNeighborHoldTimerExpired{}, e) 305 | s.failNowIfNotStateTransition(IdleState) 306 | } 307 | 308 | // advance to established state and send a keepalive 309 | func (s *fsmTestSuite) TestFSMEstablishedSendKA() { 310 | s.advanceToEstablishedState() 311 | err := s.sendKeepalive() 312 | assert.Nil(s.T(), err) 313 | } 314 | 315 | // advance to established state and send an update message 316 | // expect EventNeighborUpdateReceived 317 | func (s *fsmTestSuite) TestFSMEstablishedSendUpdate() { 318 | s.advanceToEstablishedState() 319 | u := &UpdateMessage{ 320 | PathAttrs: []PathAttr{ 321 | &PathAttrLocalPref{Preference: 100}, 322 | &PathAttrOrigin{Origin: OriginCodeIGP}, 323 | }, 324 | } 325 | b, err := u.serialize() 326 | if err != nil { 327 | assert.FailNow(s.T(), err.Error()) 328 | } 329 | _, err = s.conn.Write(b) 330 | if err != nil { 331 | assert.FailNow(s.T(), err.Error()) 332 | } 333 | 334 | e := <-s.events 335 | if assert.IsType(s.T(), &EventNeighborUpdateReceived{}, e) { 336 | assert.Equal(s.T(), e.(*EventNeighborUpdateReceived).Message, u) 337 | } 338 | } 339 | 340 | // advance to established state and send a notification message 341 | // expect EventNeighborNotificationReceived 342 | func (s *fsmTestSuite) TestFSMEstablishedSendNotif() { 343 | s.advanceToEstablishedState() 344 | n := &NotificationMessage{ 345 | Code: NotifErrCodeUpdateMessage, 346 | Subcode: NotifErrSubcodeMalformedAttr, 347 | } 348 | b, err := n.serialize() 349 | if err != nil { 350 | assert.FailNow(s.T(), err.Error()) 351 | } 352 | _, err = s.conn.Write(b) 353 | if err != nil { 354 | assert.FailNow(s.T(), err.Error()) 355 | } 356 | 357 | e := <-s.events 358 | if assert.IsType(s.T(), &EventNeighborNotificationReceived{}, e) { 359 | assert.Equal(s.T(), e.(*EventNeighborNotificationReceived).Message, n) 360 | } 361 | } 362 | 363 | // advance to established state and send an open message 364 | // expect EventNeighborErr and EventNeighborStateTransition 365 | func (s *fsmTestSuite) TestFSMEstablishedSendOpen() { 366 | s.advanceToEstablishedState() 367 | err := s.sendOpen() 368 | if err != nil { 369 | s.T().Fatal(err) 370 | } 371 | 372 | e := <-s.events 373 | assert.IsType(s.T(), &EventNeighborErr{}, e) 374 | s.failNowIfNotStateTransition(IdleState) 375 | } 376 | 377 | func TestFSM(t *testing.T) { 378 | s := &fsmTestSuite{} 379 | suite.Run(t, s) 380 | } 381 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /packet_open.go: -------------------------------------------------------------------------------- 1 | package bgpls 2 | 3 | import ( 4 | "encoding/binary" 5 | "errors" 6 | "math" 7 | "net" 8 | "time" 9 | ) 10 | 11 | func newOpenMessage(asn uint32, holdTime time.Duration, bgpID net.IP) (*openMessage, error) { 12 | o := &openMessage{ 13 | version: 4, 14 | holdTime: uint16(holdTime.Seconds()), 15 | optParams: []optParam{ 16 | &capabilityOptParam{ 17 | caps: []capability{ 18 | &capFourOctetAs{ 19 | asn: asn, 20 | }, 21 | &capMultiproto{ 22 | afi: BgpLsAfi, 23 | safi: BgpLsSafi, 24 | }, 25 | }, 26 | }, 27 | }, 28 | } 29 | 30 | switch len(bgpID) { 31 | case 4: 32 | o.bgpID = binary.BigEndian.Uint32(bgpID[0:4]) 33 | case 16: 34 | o.bgpID = binary.BigEndian.Uint32(bgpID[12:16]) 35 | default: 36 | return nil, errors.New("invalid bgp ID") 37 | } 38 | 39 | /* 40 | rfc4893 page2 41 | To represent 4-octet AS numbers (which are not mapped from 2-octets) 42 | as 2-octet AS numbers in the AS path information encoded with 2-octet 43 | AS numbers, this document reserves a 2-octet AS number. We denote 44 | this special AS number as AS_TRANS for ease of description in the 45 | rest of this specification. This AS number is also placed in the "My 46 | Autonomous System" field of the OPEN message originated by a NEW BGP 47 | speaker, if the speaker does not have a (globally unique) 2-octet AS 48 | number. 49 | */ 50 | if asn > math.MaxUint16 { 51 | o.asn = asTrans 52 | } else { 53 | o.asn = uint16(asn) 54 | } 55 | 56 | return o, nil 57 | } 58 | 59 | type openMessage struct { 60 | version uint8 61 | asn uint16 62 | holdTime uint16 63 | bgpID uint32 64 | optParams []optParam 65 | } 66 | 67 | const ( 68 | asTrans uint16 = 23456 69 | ) 70 | 71 | func (o *openMessage) MessageType() MessageType { 72 | return OpenMessageType 73 | } 74 | 75 | func (o *openMessage) serialize() ([]byte, error) { 76 | buff := make([]byte, 9) 77 | 78 | // version 79 | buff[0] = o.version 80 | 81 | // asn 82 | binary.BigEndian.PutUint16(buff[1:3], o.asn) 83 | 84 | // holdTime 85 | binary.BigEndian.PutUint16(buff[3:5], o.holdTime) 86 | 87 | // bgpID 88 | binary.BigEndian.PutUint32(buff[5:9], o.bgpID) 89 | 90 | // optional parameters 91 | params := make([]byte, 0, 512) 92 | for _, p := range o.optParams { 93 | b, err := p.serialize() 94 | if err != nil { 95 | return buff, err 96 | } 97 | 98 | params = append(params, b...) 99 | } 100 | 101 | buff = append(buff, uint8(len(params))) 102 | buff = append(buff, params...) 103 | 104 | buff = prependHeader(buff, OpenMessageType) 105 | 106 | return buff, nil 107 | } 108 | 109 | func (o *openMessage) deserialize(b []byte) error { 110 | if len(b) < 10 { 111 | return &errWithNotification{ 112 | error: errors.New("open message too short"), 113 | code: NotifErrCodeOpenMessage, 114 | subcode: NotifErrSubcodeBadLength, 115 | } 116 | } 117 | 118 | // version 119 | o.version = b[0] 120 | 121 | // asn 122 | o.asn = binary.BigEndian.Uint16(b[1:3]) 123 | 124 | // hold time 125 | o.holdTime = binary.BigEndian.Uint16(b[3:5]) 126 | 127 | // bgpID 128 | o.bgpID = binary.BigEndian.Uint32(b[5:9]) 129 | 130 | // optional parameters 131 | optParamsLen := int(b[9]) 132 | if optParamsLen != len(b)-10 { 133 | return &errWithNotification{ 134 | error: errors.New("optional parameter length field does not match actual length"), 135 | code: NotifErrCodeOpenMessage, 136 | subcode: 0, 137 | } 138 | } 139 | 140 | optParams, err := deserializeOptParams(b[10:]) 141 | if err != nil { 142 | return err 143 | } 144 | 145 | o.optParams = optParams 146 | 147 | return nil 148 | } 149 | 150 | func deserializeOptParams(b []byte) ([]optParam, error) { 151 | params := make([]optParam, 0, 1) 152 | 153 | for { 154 | if len(b) < 2 { 155 | return nil, &errWithNotification{ 156 | error: errors.New("optional parameter too short"), 157 | code: NotifErrCodeOpenMessage, 158 | subcode: 0, 159 | } 160 | } 161 | 162 | paramCode := b[0] 163 | paramLen := b[1] 164 | if len(b) < int(paramLen)+2 { 165 | return nil, &errWithNotification{ 166 | error: errors.New("optional parameter length does not match length field"), 167 | code: NotifErrCodeOpenMessage, 168 | subcode: 0, 169 | } 170 | } 171 | 172 | paramToDecode := make([]byte, 0) 173 | if paramLen > 0 { 174 | paramToDecode = b[2 : paramLen+2] 175 | } 176 | 177 | nextParam := 2 + int(paramLen) 178 | b = b[nextParam:] 179 | 180 | switch paramCode { 181 | case uint8(capabilityOptParamType): 182 | cap := &capabilityOptParam{} 183 | 184 | err := cap.deserialize(paramToDecode) 185 | if err != nil { 186 | return nil, err 187 | } 188 | 189 | params = append(params, cap) 190 | default: 191 | } 192 | 193 | if len(b) == 0 { 194 | break 195 | } 196 | } 197 | 198 | return params, nil 199 | } 200 | 201 | func validateOpenMessage(msg *openMessage, neighborASN uint32) error { 202 | if msg.version != 4 { 203 | version := make([]byte, 2) 204 | binary.BigEndian.PutUint16(version, uint16(4)) 205 | return &errWithNotification{ 206 | error: errors.New("unsupported version number"), 207 | code: NotifErrCodeOpenMessage, 208 | subcode: NotifErrSubcodeUnsupportedVersionNumber, 209 | data: version, 210 | } 211 | } 212 | 213 | var fourOctetAS, fourOctetAsFound, bgpLsAfFound bool 214 | if msg.asn == asTrans { 215 | fourOctetAS = true 216 | } else { 217 | if msg.asn != uint16(neighborASN) { 218 | return &errWithNotification{ 219 | error: errors.New("bad peer AS"), 220 | code: NotifErrCodeOpenMessage, 221 | subcode: NotifErrSubcodeBadPeerAS, 222 | } 223 | } 224 | } 225 | 226 | if msg.holdTime < 3 { 227 | return &errWithNotification{ 228 | error: errors.New("hold time must be >=3"), 229 | code: NotifErrCodeOpenMessage, 230 | subcode: NotifErrSubcodeUnacceptableHoldTime, 231 | } 232 | } 233 | 234 | if msg.bgpID == 0 { 235 | return &errWithNotification{ 236 | error: errors.New("bgp ID cannot be 0"), 237 | code: NotifErrCodeOpenMessage, 238 | subcode: NotifErrSubcodeBadBgpID, 239 | } 240 | } 241 | 242 | for _, p := range msg.optParams { 243 | capOptParam, isCapability := p.(*capabilityOptParam) 244 | if !isCapability { 245 | return &errWithNotification{ 246 | error: errors.New("non-capability optional parameter found"), 247 | code: NotifErrCodeOpenMessage, 248 | subcode: NotifErrSubcodeUnsupportedOptParam, 249 | } 250 | } 251 | 252 | for _, c := range capOptParam.caps { 253 | switch cap := c.(type) { 254 | case *capFourOctetAs: 255 | fourOctetAsFound = true 256 | if cap.asn != neighborASN { 257 | return &errWithNotification{ 258 | error: errors.New("bad peer AS"), 259 | code: NotifErrCodeOpenMessage, 260 | subcode: NotifErrSubcodeBadPeerAS, 261 | } 262 | } 263 | case *capMultiproto: 264 | if cap.afi == BgpLsAfi && cap.safi == BgpLsSafi { 265 | bgpLsAfFound = true 266 | } 267 | case *capUnknown: 268 | } 269 | } 270 | } 271 | 272 | if !bgpLsAfFound { 273 | bgpLsCap := &capMultiproto{ 274 | afi: BgpLsAfi, 275 | safi: BgpLsSafi, 276 | } 277 | b, err := bgpLsCap.serialize() 278 | if err != nil { 279 | panic("error serializing bgp-ls multiprotocol capability") 280 | } 281 | return &errWithNotification{ 282 | error: errors.New("bgp-ls capability not found"), 283 | code: NotifErrCodeOpenMessage, 284 | subcode: NotifErrSubcodeUnsupportedCapability, 285 | data: b, 286 | } 287 | } 288 | 289 | if fourOctetAS && !fourOctetAsFound { 290 | return &errWithNotification{ 291 | error: errors.New("4-octet AS indicated in as field but not found in capabilities"), 292 | code: NotifErrCodeOpenMessage, 293 | subcode: NotifErrSubcodeBadPeerAS, 294 | } 295 | } 296 | 297 | return nil 298 | } 299 | 300 | type optParamType uint8 301 | 302 | const ( 303 | capabilityOptParamType optParamType = 2 304 | ) 305 | 306 | type optParam interface { 307 | optParamType() optParamType 308 | serialize() ([]byte, error) 309 | deserialize(b []byte) error 310 | } 311 | 312 | type capabilityOptParam struct { 313 | caps []capability 314 | } 315 | 316 | func (c *capabilityOptParam) optParamType() optParamType { 317 | return capabilityOptParamType 318 | } 319 | 320 | func (c *capabilityOptParam) serialize() ([]byte, error) { 321 | buff := make([]byte, 0, 512) 322 | caps := make([]byte, 0, 512) 323 | 324 | if len(c.caps) > 0 { 325 | for _, c := range c.caps { 326 | b, err := c.serialize() 327 | if err != nil { 328 | return buff, err 329 | } 330 | caps = append(caps, b...) 331 | } 332 | } else { 333 | return buff, errors.New("empty caps in cap opt param") 334 | } 335 | 336 | buff = append(buff, uint8(capabilityOptParamType)) 337 | buff = append(buff, uint8(len(caps))) 338 | buff = append(buff, caps...) 339 | 340 | return buff, nil 341 | } 342 | 343 | func (c *capabilityOptParam) deserialize(b []byte) error { 344 | for { 345 | if len(b) < 2 { 346 | return &errWithNotification{ 347 | error: errors.New("capability too short"), 348 | code: NotifErrCodeOpenMessage, 349 | subcode: 0, 350 | } 351 | } 352 | 353 | capCode := b[0] 354 | capLen := b[1] 355 | if len(b) < int(capLen)+2 { 356 | return &errWithNotification{ 357 | error: errors.New("capability length does not match length field"), 358 | code: NotifErrCodeOpenMessage, 359 | subcode: 0, 360 | } 361 | } 362 | 363 | capToDecode := make([]byte, 0) 364 | if capLen > 0 { 365 | capToDecode = b[2 : capLen+2] 366 | } 367 | 368 | nextCap := 2 + int(capLen) 369 | b = b[nextCap:] 370 | 371 | switch capCode { 372 | case uint8(capCodeMultiproto): 373 | cap := &capMultiproto{} 374 | err := cap.deserialize(capToDecode) 375 | if err != nil { 376 | return err 377 | } 378 | 379 | c.caps = append(c.caps, cap) 380 | case uint8(capCodeFourOctetAs): 381 | cap := &capFourOctetAs{} 382 | err := cap.deserialize(capToDecode) 383 | if err != nil { 384 | return err 385 | } 386 | 387 | c.caps = append(c.caps, cap) 388 | default: 389 | cap := &capUnknown{ 390 | code: capCode, 391 | } 392 | err := cap.deserialize(capToDecode) 393 | if err != nil { 394 | return err 395 | } 396 | 397 | c.caps = append(c.caps, cap) 398 | } 399 | 400 | if len(b) == 0 { 401 | return nil 402 | } 403 | } 404 | } 405 | 406 | type capabilityCode uint8 407 | 408 | const ( 409 | capCodeMultiproto capabilityCode = 1 410 | capCodeFourOctetAs capabilityCode = 65 411 | ) 412 | 413 | type capability interface { 414 | capabilityCode() capabilityCode 415 | serialize() ([]byte, error) 416 | deserialize(b []byte) error 417 | } 418 | 419 | type capUnknown struct { 420 | code uint8 421 | data []byte 422 | } 423 | 424 | func (u *capUnknown) capabilityCode() capabilityCode { 425 | return capabilityCode(u.code) 426 | } 427 | 428 | func (u *capUnknown) serialize() ([]byte, error) { 429 | buff := make([]byte, 2) 430 | buff[0] = u.code 431 | buff[1] = uint8(len(u.data)) 432 | 433 | if len(u.data) > 0 { 434 | buff = append(buff, u.data...) 435 | } 436 | 437 | return buff, nil 438 | } 439 | 440 | func (u *capUnknown) deserialize(b []byte) error { 441 | if len(b) > 2 { 442 | u.data = b[2:] 443 | } 444 | 445 | return nil 446 | } 447 | 448 | // MultiprotoAfi identifies the address family for multiprotocol bgp. 449 | type MultiprotoAfi uint16 450 | 451 | // MultiprotoAfi values 452 | const ( 453 | BgpLsAfi MultiprotoAfi = 16388 454 | ) 455 | 456 | // MultiprotoSafi identifies the subsequent address family for multiprotocol bgp. 457 | type MultiprotoSafi uint8 458 | 459 | // MultiprotoSafi values 460 | const ( 461 | BgpLsSafi MultiprotoSafi = 71 462 | ) 463 | 464 | type capMultiproto struct { 465 | afi MultiprotoAfi 466 | safi MultiprotoSafi 467 | } 468 | 469 | func (m *capMultiproto) serialize() ([]byte, error) { 470 | buff := make([]byte, 6) 471 | 472 | // type 473 | buff[0] = uint8(capCodeMultiproto) 474 | 475 | // length 476 | buff[1] = uint8(4) 477 | 478 | // afi 479 | binary.BigEndian.PutUint16(buff[2:4], uint16(m.afi)) 480 | 481 | // reserved 482 | buff[4] = 0 483 | 484 | // safi 485 | buff[5] = uint8(m.safi) 486 | 487 | return buff, nil 488 | } 489 | 490 | func (m *capMultiproto) deserialize(b []byte) error { 491 | if len(b) != 4 { 492 | return &errWithNotification{ 493 | error: errors.New("multiprotocol capability length does not equal 4"), 494 | code: NotifErrCodeOpenMessage, 495 | subcode: 0, 496 | } 497 | } 498 | 499 | m.afi = MultiprotoAfi(binary.BigEndian.Uint16(b[:2])) 500 | m.safi = MultiprotoSafi(b[3]) 501 | 502 | return nil 503 | } 504 | 505 | func (m *capMultiproto) capabilityCode() capabilityCode { 506 | return capCodeMultiproto 507 | } 508 | 509 | type capFourOctetAs struct { 510 | asn uint32 511 | } 512 | 513 | func (f *capFourOctetAs) serialize() ([]byte, error) { 514 | buff := make([]byte, 6) 515 | 516 | // type 517 | buff[0] = uint8(capCodeFourOctetAs) 518 | 519 | // length 520 | buff[1] = uint8(4) 521 | 522 | // asn 523 | binary.BigEndian.PutUint32(buff[2:], f.asn) 524 | 525 | return buff, nil 526 | } 527 | 528 | func (f *capFourOctetAs) deserialize(b []byte) error { 529 | if len(b) != 4 { 530 | return &errWithNotification{ 531 | error: errors.New("4-octet AS capability length does not equal 4"), 532 | code: NotifErrCodeOpenMessage, 533 | subcode: 0, 534 | } 535 | } 536 | 537 | f.asn = binary.BigEndian.Uint32(b) 538 | 539 | return nil 540 | } 541 | 542 | func (f *capFourOctetAs) capabilityCode() capabilityCode { 543 | return capCodeFourOctetAs 544 | } 545 | -------------------------------------------------------------------------------- /fsm.go: -------------------------------------------------------------------------------- 1 | package bgpls 2 | 3 | import ( 4 | "context" 5 | "errors" 6 | "fmt" 7 | "net" 8 | "strconv" 9 | "sync" 10 | "time" 11 | ) 12 | 13 | // FSMState describes the state of a neighbor's fsm 14 | type FSMState uint8 15 | 16 | // FSMState values 17 | const ( 18 | DisabledState FSMState = iota 19 | IdleState 20 | ConnectState 21 | ActiveState 22 | OpenSentState 23 | OpenConfirmState 24 | EstablishedState 25 | ) 26 | 27 | func (s FSMState) String() string { 28 | switch s { 29 | case DisabledState: 30 | return "disabled" 31 | case IdleState: 32 | return "idle" 33 | case ConnectState: 34 | return "connect" 35 | case ActiveState: 36 | return "active" 37 | case OpenSentState: 38 | return "openSent" 39 | case OpenConfirmState: 40 | return "openConfirm" 41 | case EstablishedState: 42 | return "established" 43 | default: 44 | return "unknown state" 45 | } 46 | } 47 | 48 | var ( 49 | errInvalidStateTransition = errors.New("invalid state transition") 50 | ) 51 | 52 | var ( 53 | // A HoldTimer value of 4 minutes is suggested. 54 | longHoldTime = time.Minute * 4 55 | ) 56 | 57 | const ( 58 | // The exact value of the ConnectRetryTimer is a local matter, but it 59 | // SHOULD be sufficiently large to allow TCP initialization. 60 | connectRetryTime = time.Second * 5 61 | ) 62 | 63 | type fsm interface { 64 | idle() FSMState 65 | connect() FSMState 66 | openSent() FSMState 67 | openConfirm() FSMState 68 | established() FSMState 69 | terminate() 70 | } 71 | 72 | type standardFSM struct { 73 | port int 74 | events chan Event 75 | disable chan interface{} 76 | neighborConfig *NeighborConfig 77 | routerID net.IP 78 | localASN uint32 79 | conn net.Conn 80 | readerErr chan error 81 | closeReader chan struct{} 82 | readerClosed chan struct{} 83 | msgCh chan Message 84 | keepAliveTime time.Duration 85 | keepAliveTimer *time.Timer 86 | holdTime time.Duration 87 | holdTimer *time.Timer 88 | connectRetryTimer *time.Timer 89 | running bool 90 | outboundConnErr chan error 91 | outboundConn chan net.Conn 92 | cancelOutboundDial context.CancelFunc 93 | *sync.Mutex 94 | } 95 | 96 | func newFSM(c *NeighborConfig, events chan Event, routerID net.IP, localASN uint32, port int) fsm { 97 | f := &standardFSM{ 98 | port: port, 99 | events: events, 100 | disable: make(chan interface{}), 101 | neighborConfig: c, 102 | routerID: routerID, 103 | localASN: localASN, 104 | keepAliveTime: time.Duration(int64(c.HoldTime) / 3).Truncate(time.Second), 105 | keepAliveTimer: time.NewTimer(0), 106 | holdTime: c.HoldTime, 107 | holdTimer: time.NewTimer(0), 108 | connectRetryTimer: time.NewTimer(0), 109 | Mutex: &sync.Mutex{}, 110 | } 111 | 112 | // drain all timers so they can be reset 113 | drainTimers(f.keepAliveTimer, f.holdTimer, f.connectRetryTimer) 114 | 115 | f.running = true 116 | go f.loop() 117 | 118 | return f 119 | } 120 | 121 | func (f *standardFSM) terminate() { 122 | f.Lock() 123 | defer f.Unlock() 124 | if !f.running { 125 | return 126 | } 127 | 128 | f.disable <- nil 129 | <-f.disable 130 | f.running = false 131 | } 132 | 133 | func (f *standardFSM) dialNeighbor() { 134 | dialer := &net.Dialer{} 135 | ctx, cancel := context.WithCancel(context.Background()) 136 | f.outboundConnErr = make(chan error) 137 | f.outboundConn = make(chan net.Conn) 138 | f.cancelOutboundDial = cancel 139 | 140 | go func() { 141 | conn, err := dialer.DialContext(ctx, "tcp", net.JoinHostPort(f.neighborConfig.Address.String(), strconv.Itoa(f.port))) 142 | if err != nil { 143 | f.outboundConnErr <- err 144 | return 145 | } 146 | 147 | f.outboundConn <- conn 148 | }() 149 | } 150 | 151 | func (f *standardFSM) startReader() { 152 | f.readerErr = make(chan error) 153 | f.closeReader = make(chan struct{}) 154 | f.readerClosed = make(chan struct{}) 155 | f.msgCh = make(chan Message) 156 | go f.read() 157 | } 158 | 159 | func (f *standardFSM) idle() FSMState { 160 | // starts the ConnectRetryTimer with the initial value 161 | f.connectRetryTimer.Reset(connectRetryTime) 162 | 163 | // initiates a TCP connection to the other BGP peer 164 | f.dialNeighbor() 165 | 166 | // changes its state to Connect 167 | return ConnectState 168 | } 169 | 170 | // cleanupConnAndReader closes the connection, 171 | // the reader close signal channel, and the messages channel 172 | func (f *standardFSM) cleanupConnAndReader() { 173 | f.conn.Close() 174 | close(f.closeReader) 175 | <-f.readerClosed 176 | close(f.msgCh) 177 | } 178 | 179 | func (f *standardFSM) connect() FSMState { 180 | Loop: 181 | for { 182 | select { 183 | case <-f.disable: 184 | drainTimers(f.connectRetryTimer) 185 | // drain the dialer and transition to DisabledState 186 | f.cancelOutboundDial() 187 | select { 188 | case <-f.outboundConn: 189 | case <-f.outboundConnErr: 190 | } 191 | return DisabledState 192 | case <-f.connectRetryTimer.C: 193 | /* 194 | In response to the ConnectRetryTimer_Expires event (Event 9), the 195 | local system: 196 | - drops the TCP connection, 197 | - restarts the ConnectRetryTimer, 198 | - stops the DelayOpenTimer and resets the timer to zero, 199 | - initiates a TCP connection to the other BGP peer, 200 | - continues to listen for a connection that may be initiated by 201 | the remote BGP peer, and 202 | - stays in the Connect state. 203 | */ 204 | f.cancelOutboundDial() 205 | // canceling races with the dialer so it must be drained 206 | select { 207 | case conn := <-f.outboundConn: 208 | f.conn = conn 209 | f.startReader() 210 | break Loop 211 | case <-f.outboundConnErr: 212 | } 213 | // timer already drained 214 | f.connectRetryTimer.Reset(connectRetryTime) 215 | f.dialNeighbor() 216 | case err := <-f.outboundConnErr: 217 | /* 218 | If the TCP connection fails (Event 18), the local system checks 219 | the DelayOpenTimer. If the DelayOpenTimer is running, the local 220 | system: 221 | - restarts the ConnectRetryTimer with the initial value, 222 | - stops the DelayOpenTimer and resets its value to zero, 223 | - continues to listen for a connection that may be initiated by 224 | the remote BGP peer, and 225 | - changes its state to Active. 226 | */ 227 | drainTimers(f.connectRetryTimer) 228 | next := f.handleErr(fmt.Errorf("error connecting to neighbor: %v", err), ActiveState) 229 | if next != DisabledState { 230 | f.connectRetryTimer.Reset(connectRetryTime) 231 | } 232 | return next 233 | case conn := <-f.outboundConn: 234 | /* 235 | If the TCP connection succeeds (Event 16 or Event 17), the local 236 | system checks the DelayOpen attribute prior to processing. 237 | ... 238 | If the DelayOpen attribute is set to FALSE, the local system: 239 | - stops the ConnectRetryTimer (if running) and sets the 240 | ConnectRetryTimer to zero, 241 | - completes BGP initialization 242 | - sends an OPEN message to its peer, 243 | - sets the HoldTimer to a large value, and 244 | - changes its state to OpenSent. 245 | */ 246 | drainTimers(f.connectRetryTimer) 247 | f.conn = conn 248 | f.startReader() 249 | break Loop 250 | } 251 | } 252 | 253 | o, err := newOpenMessage(f.localASN, f.holdTime, f.routerID) 254 | if err != nil { 255 | f.cleanupConnAndReader() 256 | return f.handleErr(fmt.Errorf("error creating open message: %v", err), IdleState) 257 | } 258 | b, err := o.serialize() 259 | if err != nil { 260 | panic("bug serializing open message") 261 | } 262 | 263 | _, err = f.conn.Write(b) 264 | if err != nil { 265 | f.cleanupConnAndReader() 266 | return f.handleErr(fmt.Errorf("error sending open message: %v", err), IdleState) 267 | } 268 | 269 | f.holdTimer.Reset(longHoldTime) 270 | 271 | return OpenSentState 272 | } 273 | 274 | func (f *standardFSM) active() FSMState { 275 | select { 276 | case <-f.disable: 277 | drainTimers(f.connectRetryTimer) 278 | return DisabledState 279 | case <-f.connectRetryTimer.C: 280 | /* 281 | In response to a ConnectRetryTimer_Expires event (Event 9), the 282 | local system: 283 | - restarts the ConnectRetryTimer (with initial value), 284 | - initiates a TCP connection to the other BGP peer, 285 | - continues to listen for a TCP connection that may be initiated 286 | by a remote BGP peer, and 287 | - changes its state to Connect. 288 | */ 289 | f.connectRetryTimer.Reset(connectRetryTime) 290 | f.dialNeighbor() 291 | return ConnectState 292 | } 293 | } 294 | 295 | // sendEvent sends the provided event on the events channel and 296 | // returns the provided FSMState unless a disable signal is received 297 | // in which case DisabledState is returned 298 | func (f *standardFSM) sendEvent(e Event, nextState FSMState) FSMState { 299 | select { 300 | case f.events <- e: 301 | return nextState 302 | case <-f.disable: 303 | return DisabledState 304 | } 305 | } 306 | 307 | // handlerErr checks the provided err to see if a notification can be unwrapped 308 | // and if so, sends it to the neighbor. 309 | // 310 | // The provided FSMState is returned unless a disable signal is received while 311 | // trying to send on the events channel in which case DisabledState is returned. 312 | func (f *standardFSM) handleErr(err error, nextState FSMState) FSMState { 313 | if err, ok := err.(*errWithNotification); ok { 314 | f.sendNotification(err.code, err.subcode, err.data) 315 | } 316 | 317 | return f.sendEvent(newEventNeighborErr(f.neighborConfig, err), nextState) 318 | } 319 | 320 | func (f *standardFSM) handleHoldTimerExpired() FSMState { 321 | /* 322 | If the HoldTimer_Expires (Event 10), the local system: 323 | - sends a NOTIFICATION message with the error code Hold Timer 324 | Expired, 325 | - sets the ConnectRetryTimer to zero, 326 | - releases all BGP resources, 327 | - drops the TCP connection, 328 | - increments the ConnectRetryCounter, 329 | - (optionally) performs peer oscillation damping if the 330 | DampPeerOscillations attribute is set to TRUE, and 331 | - changes its state to Idle. 332 | */ 333 | f.sendHoldTimerExpired() 334 | f.cleanupConnAndReader() 335 | 336 | return f.sendEvent(newEventNeighborHoldTimerExpired(f.neighborConfig), IdleState) 337 | } 338 | 339 | func (f *standardFSM) read() { 340 | defer close(f.readerClosed) 341 | 342 | for { 343 | select { 344 | case <-f.closeReader: 345 | return 346 | default: 347 | buff := make([]byte, 4096) 348 | n, err := f.conn.Read(buff) 349 | if err != nil { 350 | select { 351 | case f.readerErr <- err: 352 | case <-f.closeReader: 353 | } 354 | return 355 | } 356 | buff = buff[:n] 357 | 358 | msgs, err := messagesFromBytes(buff) 359 | if err != nil { 360 | select { 361 | case f.readerErr <- err: 362 | case <-f.closeReader: 363 | } 364 | 365 | return 366 | } 367 | 368 | for _, m := range msgs { 369 | select { 370 | case f.msgCh <- m: 371 | case <-f.closeReader: 372 | return 373 | } 374 | } 375 | } 376 | } 377 | } 378 | 379 | func (f *standardFSM) sendHoldTimerExpired() error { 380 | return f.sendNotification(NotifErrCodeHoldTimerExpired, 0, nil) 381 | } 382 | 383 | // handleUnexpectedMessageType sends the appropriate notification message to the 384 | // neighbor and generates an EventNeighborErr 385 | func (f *standardFSM) handleUnexpectedMessageType(received MessageType, next FSMState) FSMState { 386 | b := make([]byte, 1) 387 | b[0] = uint8(received) 388 | f.sendNotification(NotifErrCodeMessageHeader, NotifErrSubcodeBadType, b) 389 | return f.sendEvent(newEventNeighborErr(f.neighborConfig, fmt.Errorf("unexpected message type: %s", received)), next) 390 | } 391 | 392 | func (f *standardFSM) openSent() FSMState { 393 | select { 394 | case <-f.disable: 395 | f.sendCease() 396 | drainTimers(f.holdTimer) 397 | f.cleanupConnAndReader() 398 | return DisabledState 399 | case err := <-f.readerErr: 400 | /* 401 | If a TcpConnectionFails event (Event 18) is received, the local 402 | system: 403 | - closes the BGP connection, 404 | - restarts the ConnectRetryTimer, 405 | - continues to listen for a connection that may be initiated by 406 | the remote BGP peer, and 407 | - changes its state to Active. 408 | */ 409 | var next FSMState 410 | // check if err is connection related or not - Active vs Idle 411 | _, isOpError := err.(*net.OpError) 412 | if isOpError { 413 | next = f.handleErr(err, ActiveState) 414 | if next != DisabledState { 415 | f.connectRetryTimer.Reset(connectRetryTime) 416 | } 417 | } else { 418 | next = f.handleErr(err, IdleState) 419 | } 420 | drainTimers(f.holdTimer) 421 | f.cleanupConnAndReader() 422 | return next 423 | case <-f.holdTimer.C: 424 | return f.handleHoldTimerExpired() 425 | case m := <-f.msgCh: 426 | open, isOpen := m.(*openMessage) 427 | if !isOpen { 428 | var next FSMState 429 | notif, isNotif := m.(*NotificationMessage) 430 | if isNotif { 431 | next = f.sendEvent(newEventNeighborNotificationReceived(f.neighborConfig, notif), IdleState) 432 | } else { 433 | next = f.handleUnexpectedMessageType(m.MessageType(), IdleState) 434 | } 435 | 436 | drainTimers(f.holdTimer) 437 | f.cleanupConnAndReader() 438 | return next 439 | } 440 | 441 | err := validateOpenMessage(open, f.neighborConfig.ASN) 442 | if err != nil { 443 | next := f.handleErr(err, IdleState) 444 | drainTimers(f.holdTimer) 445 | f.cleanupConnAndReader() 446 | return next 447 | } 448 | 449 | if float64(open.holdTime) < f.holdTime.Seconds() { 450 | f.holdTime = time.Duration(int64(open.holdTime) * int64(time.Second)) 451 | f.keepAliveTime = (f.holdTime / 3).Truncate(time.Second) 452 | } 453 | 454 | err = f.sendKeepAlive() 455 | if err != nil { 456 | next := f.handleErr(err, IdleState) 457 | drainTimers(f.holdTimer) 458 | f.cleanupConnAndReader() 459 | return next 460 | } 461 | 462 | f.drainAndResetHoldTimer() 463 | return OpenConfirmState 464 | } 465 | } 466 | 467 | func (f *standardFSM) sendKeepAlive() error { 468 | ka := &keepAliveMessage{} 469 | b, err := ka.serialize() 470 | if err != nil { 471 | panic("bug serializing keepalive message") 472 | } 473 | _, err = f.conn.Write(b) 474 | return err 475 | } 476 | 477 | func (f *standardFSM) openConfirm() FSMState { 478 | for { 479 | select { 480 | case <-f.disable: 481 | f.sendCease() 482 | drainTimers(f.holdTimer) 483 | f.cleanupConnAndReader() 484 | return DisabledState 485 | case err := <-f.readerErr: 486 | next := f.handleErr(err, IdleState) 487 | drainTimers(f.holdTimer) 488 | f.cleanupConnAndReader() 489 | return next 490 | case <-f.holdTimer.C: 491 | return f.handleHoldTimerExpired() 492 | case m := <-f.msgCh: 493 | _, isKeepAlive := m.(*keepAliveMessage) 494 | if !isKeepAlive { 495 | next := f.handleUnexpectedMessageType(m.MessageType(), IdleState) 496 | drainTimers(f.holdTimer) 497 | f.cleanupConnAndReader() 498 | return next 499 | } 500 | 501 | f.drainAndResetHoldTimer() 502 | // does not need to be drained 503 | f.keepAliveTimer.Reset(f.keepAliveTime) 504 | return EstablishedState 505 | } 506 | } 507 | } 508 | 509 | func (f *standardFSM) established() FSMState { 510 | for { 511 | select { 512 | case <-f.disable: 513 | f.sendCease() 514 | drainTimers(f.keepAliveTimer, f.holdTimer) 515 | f.cleanupConnAndReader() 516 | return DisabledState 517 | case err := <-f.readerErr: 518 | next := f.handleErr(err, IdleState) 519 | drainTimers(f.keepAliveTimer, f.holdTimer) 520 | f.cleanupConnAndReader() 521 | return next 522 | case <-f.holdTimer.C: 523 | drainTimers(f.keepAliveTimer) 524 | return f.handleHoldTimerExpired() 525 | case <-f.keepAliveTimer.C: 526 | err := f.sendKeepAlive() 527 | if err != nil { 528 | next := f.handleErr(err, IdleState) 529 | drainTimers(f.holdTimer) 530 | f.cleanupConnAndReader() 531 | return next 532 | } 533 | // does not need to be drained 534 | f.keepAliveTimer.Reset(f.keepAliveTime) 535 | case m := <-f.msgCh: 536 | switch m := m.(type) { 537 | case *keepAliveMessage: 538 | f.drainAndResetHoldTimer() 539 | case *UpdateMessage: 540 | f.drainAndResetHoldTimer() 541 | next := f.sendEvent(newEventNeighborUpdateReceived(f.neighborConfig, m), EstablishedState) 542 | if next == DisabledState { 543 | f.sendCease() 544 | drainTimers(f.keepAliveTimer, f.holdTimer) 545 | f.cleanupConnAndReader() 546 | return next 547 | } 548 | case *NotificationMessage: 549 | drainTimers(f.keepAliveTimer, f.holdTimer) 550 | f.cleanupConnAndReader() 551 | return f.sendEvent(newEventNeighborNotificationReceived(f.neighborConfig, m), IdleState) 552 | case *openMessage: 553 | next := f.handleUnexpectedMessageType(m.MessageType(), IdleState) 554 | drainTimers(f.holdTimer) 555 | f.cleanupConnAndReader() 556 | return next 557 | } 558 | } 559 | } 560 | } 561 | 562 | func (f *standardFSM) loop() { 563 | var current FSMState 564 | next := IdleState 565 | 566 | for { 567 | if next != DisabledState { 568 | next = f.sendEvent(newEventNeighborStateTransition(f.neighborConfig, next), next) 569 | } 570 | 571 | current = next 572 | 573 | switch current { 574 | case DisabledState: 575 | f.disable <- nil 576 | return 577 | case IdleState: 578 | next = f.idle() 579 | case ConnectState: 580 | next = f.connect() 581 | case ActiveState: 582 | next = f.active() 583 | case OpenSentState: 584 | next = f.openSent() 585 | case OpenConfirmState: 586 | next = f.openConfirm() 587 | case EstablishedState: 588 | next = f.established() 589 | } 590 | 591 | err := validTransition(current, next) 592 | if err != nil { 593 | panic(fmt.Sprintf("invalid state transition for neighbor:%s %s to %s", f.neighborConfig.Address, current, next)) 594 | } 595 | } 596 | } 597 | 598 | func drainTimers(timers ...*time.Timer) { 599 | for _, t := range timers { 600 | if !t.Stop() { 601 | <-t.C 602 | } 603 | } 604 | } 605 | 606 | func (f *standardFSM) drainAndResetHoldTimer() { 607 | drainTimers(f.holdTimer) 608 | f.holdTimer.Reset(f.holdTime) 609 | } 610 | 611 | func (f *standardFSM) sendCease() error { 612 | return f.sendNotification(NotifErrCodeCease, 0, nil) 613 | } 614 | 615 | func (f *standardFSM) sendNotification(code NotifErrCode, subcode NotifErrSubcode, data []byte) error { 616 | n := &NotificationMessage{ 617 | Code: code, 618 | Subcode: subcode, 619 | Data: data, 620 | } 621 | 622 | b, err := n.serialize() 623 | if err != nil { 624 | return err 625 | } 626 | 627 | _, err = f.conn.Write(b) 628 | return err 629 | } 630 | 631 | func validTransition(current, next FSMState) error { 632 | switch next { 633 | case DisabledState: 634 | return nil 635 | case IdleState: 636 | return nil 637 | case ConnectState: 638 | if current == IdleState || current == ActiveState { 639 | return nil 640 | } 641 | case ActiveState: 642 | if current == ConnectState || current == OpenSentState { 643 | return nil 644 | } 645 | case OpenSentState: 646 | if current == ConnectState || current == ActiveState { 647 | return nil 648 | } 649 | case OpenConfirmState: 650 | if current == OpenSentState { 651 | return nil 652 | } 653 | case EstablishedState: 654 | if current == OpenConfirmState { 655 | return nil 656 | } 657 | } 658 | 659 | return errors.New("invalid state transition") 660 | } 661 | -------------------------------------------------------------------------------- /packet_update_test.go: -------------------------------------------------------------------------------- 1 | package bgpls 2 | 3 | import ( 4 | "encoding/binary" 5 | "net" 6 | "testing" 7 | "time" 8 | 9 | "github.com/stretchr/testify/assert" 10 | ) 11 | 12 | func TestPrefixAttrSourceRouterID(t *testing.T) { 13 | p := &PrefixAttrSourceRouterID{} 14 | assert.Equal(t, p.Code(), PrefixAttrCodeSourceRouterID) 15 | 16 | // invalid len 17 | err := p.deserialize([]byte{}) 18 | assert.NotNil(t, err) 19 | 20 | // v4 21 | err = p.deserialize([]byte{1, 1, 1, 1}) 22 | assert.Nil(t, err) 23 | _, err = p.serialize() 24 | assert.Nil(t, err) 25 | 26 | // v6 27 | err = p.deserialize([]byte{1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}) 28 | assert.Nil(t, err) 29 | _, err = p.serialize() 30 | assert.Nil(t, err) 31 | 32 | // invalid addr 33 | p.RouterID = nil 34 | _, err = p.serialize() 35 | assert.NotNil(t, err) 36 | } 37 | 38 | func TestPrefixAttrFlagsIsIs(t *testing.T) { 39 | p := &PrefixAttrFlagsIsIs{} 40 | assert.Equal(t, p.Code(), PrefixAttrCodeFlags) 41 | 42 | // invalid len 43 | err := p.deserialize([]byte{}) 44 | assert.NotNil(t, err) 45 | 46 | err = p.deserialize([]byte{224}) 47 | assert.Nil(t, err) 48 | assert.True(t, p.External) 49 | assert.True(t, p.Readvertisement) 50 | assert.True(t, p.Node) 51 | b, err := p.serialize() 52 | assert.Nil(t, err) 53 | assert.Equal(t, b[4], uint8(224)) 54 | } 55 | 56 | func TestPrefixAttrFlagsOSPFv3(t *testing.T) { 57 | p := &PrefixAttrFlagsOSPFv3{} 58 | assert.Equal(t, p.Code(), PrefixAttrCodeFlags) 59 | 60 | // invalid len 61 | err := p.deserialize([]byte{}) 62 | assert.NotNil(t, err) 63 | 64 | err = p.deserialize([]byte{27}) 65 | assert.Nil(t, err) 66 | assert.True(t, p.DN) 67 | assert.True(t, p.Propagate) 68 | assert.True(t, p.LocalAddress) 69 | assert.True(t, p.NoUnicast) 70 | b, err := p.serialize() 71 | assert.Nil(t, err) 72 | assert.Equal(t, b[4], uint8(27)) 73 | } 74 | 75 | func TestPrefixAttrFlagsOSPFv2(t *testing.T) { 76 | p := &PrefixAttrFlagsOSPFv2{} 77 | assert.Equal(t, p.Code(), PrefixAttrCodeFlags) 78 | 79 | // invalid len 80 | err := p.deserialize([]byte{}) 81 | assert.NotNil(t, err) 82 | 83 | err = p.deserialize([]byte{192}) 84 | assert.Nil(t, err) 85 | assert.True(t, p.Attach) 86 | assert.True(t, p.Node) 87 | b, err := p.serialize() 88 | assert.Nil(t, err) 89 | assert.Equal(t, b[4], uint8(192)) 90 | } 91 | 92 | func TestPrefixAttrRange(t *testing.T) { 93 | p := &PrefixAttrRange{} 94 | 95 | // invalid len 96 | err := p.deserialize([]byte{}, 0) 97 | assert.NotNil(t, err) 98 | 99 | // isis flags 100 | err = p.deserialize([]byte{0, 0, 0, 0}, LinkStateNlriIsIsL1ProtocolID) 101 | assert.Nil(t, err) 102 | 103 | // ospf flags 104 | err = p.deserialize([]byte{0, 0, 0, 0}, LinkStateNlriOSPFv2ProtocolID) 105 | assert.Nil(t, err) 106 | 107 | // invalid nlri proto 108 | err = p.deserialize([]byte{0, 0, 0, 0}, LinkStateNlriDirectProtocolID) 109 | assert.NotNil(t, err) 110 | 111 | // err deserializing attrs 112 | err = p.deserialize([]byte{0, 0, 0, 0, 0}, LinkStateNlriIsIsL1ProtocolID) 113 | assert.NotNil(t, err) 114 | 115 | // invalid attrs 116 | err = p.deserialize([]byte{0, 0, 0, 0, 1, 7, 0, 2, 0, 1}, LinkStateNlriIsIsL1ProtocolID) 117 | assert.NotNil(t, err) 118 | 119 | // invalid prefix attr 120 | err = p.deserialize([]byte{0, 0, 0, 0, 4, 128, 0, 1, 1}, LinkStateNlriIsIsL1ProtocolID) 121 | assert.NotNil(t, err) 122 | 123 | // err serializing prefix sid 124 | p.PrefixSID = []*PrefixAttrPrefixSID{ 125 | &PrefixAttrPrefixSID{ 126 | Flags: nil, 127 | }, 128 | } 129 | _, err = p.serialize() 130 | assert.NotNil(t, err) 131 | 132 | // nil flags 133 | p.Flags = nil 134 | _, err = p.serialize() 135 | assert.NotNil(t, err) 136 | } 137 | 138 | func TestPrefixAttrRangeFlags(t *testing.T) { 139 | i := &PrefixAttrRangeFlagsIsIs{} 140 | assert.Equal(t, i.Type(), PrefixAttrRangeFlagsTypeIsIs) 141 | 142 | i.deserialize(248) 143 | assert.True(t, i.AddressFamily) 144 | assert.True(t, i.Mirror) 145 | assert.True(t, i.SFlag) 146 | assert.True(t, i.DFlag) 147 | assert.True(t, i.Attached) 148 | assert.Equal(t, uint8(248), i.serialize()) 149 | 150 | o := &PrefixAttrRangeFlagsOspf{} 151 | assert.Equal(t, o.Type(), PrefixAttrRangeFlagsTypeOspf) 152 | 153 | o.deserialize(128) 154 | assert.True(t, o.InterArea) 155 | assert.Equal(t, uint8(128), o.serialize()) 156 | } 157 | 158 | func TestPrefixAttrPrefixSID(t *testing.T) { 159 | p := &PrefixAttrPrefixSID{} 160 | 161 | // isis flags 162 | err := p.deserialize([]byte{0, 0, 0, 0, 0, 0, 1}, LinkStateNlriIsIsL1ProtocolID) 163 | assert.Nil(t, err) 164 | 165 | // ospf flags 166 | err = p.deserialize([]byte{0, 0, 0, 0, 0, 0, 1}, LinkStateNlriOSPFv2ProtocolID) 167 | assert.Nil(t, err) 168 | 169 | // invalid nlri proto 170 | err = p.deserialize([]byte{0, 0, 0, 0, 0, 0, 1}, LinkStateNlriDirectProtocolID) 171 | assert.NotNil(t, err) 172 | 173 | // err deserializing SIDIndexLabel 174 | err = p.deserialize([]byte{0, 0, 0, 0, 0, 0, 1, 0, 0}, LinkStateNlriIsIsL1ProtocolID) 175 | assert.NotNil(t, err) 176 | 177 | // err serializing SIDIndexLabel 178 | p.SIDIndexLabel = &SIDIndexLabelLabel{ 179 | Label: 1 << 25, 180 | } 181 | _, err = p.serialize() 182 | assert.NotNil(t, err) 183 | 184 | // nil SIDIndexLabel 185 | p.SIDIndexLabel = nil 186 | _, err = p.serialize() 187 | assert.NotNil(t, err) 188 | 189 | // nil Flags 190 | p.Flags = nil 191 | _, err = p.serialize() 192 | assert.NotNil(t, err) 193 | } 194 | 195 | func TestPrefixAttrPrefixSIDFlags(t *testing.T) { 196 | i := &PrefixAttrPrefixSIDFlagsIsIs{} 197 | assert.Equal(t, i.Type(), PrefixAttrPrefixSIDFlagsTypeIsIs) 198 | 199 | i.deserialize(252) 200 | assert.True(t, i.Readvertisement) 201 | assert.True(t, i.NodeSID) 202 | assert.True(t, i.NoPHP) 203 | assert.True(t, i.ExplicitNull) 204 | assert.True(t, i.Value) 205 | assert.True(t, i.Local) 206 | assert.Equal(t, uint8(252), i.serialize()) 207 | 208 | o := &PrefixAttrPrefixSIDFlagsOspf{} 209 | assert.Equal(t, o.Type(), PrefixAttrPrefixSIDFlagsTypeOspf) 210 | 211 | o.deserialize(248) 212 | assert.True(t, o.NoPHP) 213 | assert.True(t, o.MappingServer) 214 | assert.True(t, o.ExplicitNull) 215 | assert.True(t, o.Value) 216 | assert.True(t, o.Local) 217 | assert.Equal(t, uint8(248), o.serialize()) 218 | } 219 | 220 | func TestLinkAttrL2BundleMember(t *testing.T) { 221 | l := &LinkAttrL2BundleMember{} 222 | 223 | // err deserializing attrs 224 | err := l.deserialize([]byte{0, 0, 0, 0, 0, 0, 0, 0}, 0) 225 | assert.NotNil(t, err) 226 | 227 | // invalid attrs 228 | err = l.deserialize([]byte{0, 0, 0, 0, 1, 7, 0, 2, 0, 1}, LinkStateNlriOSPFv2ProtocolID) 229 | assert.NotNil(t, err) 230 | 231 | // err serializing link attrs 232 | l.LinkAttrs = append(l.LinkAttrs, &LinkAttrUniPacketLoss{ 233 | LossPercent: 1 << 25, 234 | }) 235 | _, err = l.serialize() 236 | assert.NotNil(t, err) 237 | } 238 | 239 | func TestFloat32Serialization(t *testing.T) { 240 | // invalid len 241 | _, err := deserializeFloat32([]byte{}) 242 | assert.NotNil(t, err) 243 | } 244 | 245 | func TestLinkAttrUniPacketLoss(t *testing.T) { 246 | // overflows 3 octets 247 | l := &LinkAttrUniPacketLoss{ 248 | LossPercent: 1 << 25, 249 | Anomalous: true, 250 | } 251 | _, err := l.serialize() 252 | assert.NotNil(t, err) 253 | 254 | // invalid loss percent 255 | l.LossPercent = 1 256 | _, err = l.serialize() 257 | assert.NotNil(t, err) 258 | } 259 | 260 | func TestLinkAttrUniDelayVariation(t *testing.T) { 261 | // overflows 3 octets 262 | l := &LinkAttrUniDelayVariation{ 263 | DelayVariation: time.Microsecond * 1 << 25, 264 | } 265 | _, err := l.serialize() 266 | assert.NotNil(t, err) 267 | } 268 | 269 | func TestLinkAttrMinMaxUniLinkDelay(t *testing.T) { 270 | // overflows 3 octets 271 | l := &LinkAttrMinMaxUniLinkDelay{ 272 | MaxDelay: time.Microsecond * 1 << 25, 273 | } 274 | _, err := l.serialize() 275 | assert.NotNil(t, err) 276 | l.MinDelay = time.Microsecond * 1 << 25 277 | _, err = l.serialize() 278 | assert.NotNil(t, err) 279 | } 280 | 281 | func TestLinkAttrUniLinkDelay(t *testing.T) { 282 | // overflows 3 octets 283 | l := &LinkAttrUniLinkDelay{ 284 | Delay: time.Microsecond * 1 << 25, 285 | } 286 | _, err := l.serialize() 287 | assert.NotNil(t, err) 288 | } 289 | 290 | func TestMicrosecondDelaySerialization(t *testing.T) { 291 | // invalid len 292 | _, err := deserializeMicrosecondDelay([]byte{}) 293 | assert.NotNil(t, err) 294 | 295 | // overflows 3 octets 296 | _, err = serializeMicrosecondDelay(time.Microsecond * 1 << 25) 297 | assert.NotNil(t, err) 298 | } 299 | 300 | func TestLinkAttrPeerSetSID(t *testing.T) { 301 | l := &LinkAttrPeerSetSID{} 302 | 303 | // invalid len 304 | err := l.deserialize([]byte{}) 305 | assert.NotNil(t, err) 306 | 307 | // err deserializing SIDIndexLabel 308 | err = l.deserialize([]byte{0, 0, 0, 0, 0, 0, 0, 0, 0}) 309 | assert.NotNil(t, err) 310 | 311 | // nil SIDIndexLabel 312 | _, err = l.serialize() 313 | assert.NotNil(t, err) 314 | 315 | // err serializing SIDIndexLabel 316 | l.SIDIndexLabel = &SIDIndexLabelLabel{ 317 | Label: 1 << 25, 318 | } 319 | _, err = l.serialize() 320 | assert.NotNil(t, err) 321 | } 322 | 323 | func TestLinkAttrPeerAdjSID(t *testing.T) { 324 | l := &LinkAttrPeerAdjSID{} 325 | 326 | // invalid len 327 | err := l.deserialize([]byte{}) 328 | assert.NotNil(t, err) 329 | 330 | // err deserializing SIDIndexLabel 331 | err = l.deserialize([]byte{0, 0, 0, 0, 0, 0, 0, 0, 0}) 332 | assert.NotNil(t, err) 333 | 334 | // nil SIDIndexLabel 335 | _, err = l.serialize() 336 | assert.NotNil(t, err) 337 | 338 | // err serializing SIDIndexLabel 339 | l.SIDIndexLabel = &SIDIndexLabelLabel{ 340 | Label: 1 << 25, 341 | } 342 | _, err = l.serialize() 343 | assert.NotNil(t, err) 344 | } 345 | 346 | func TestLinkAttrPeerNodeSID(t *testing.T) { 347 | l := &LinkAttrPeerNodeSID{} 348 | 349 | // invalid len 350 | err := l.deserialize([]byte{}) 351 | assert.NotNil(t, err) 352 | 353 | // err deserializing SIDIndexLabel 354 | err = l.deserialize([]byte{0, 0, 0, 0, 0, 0, 0, 0, 0}) 355 | assert.NotNil(t, err) 356 | 357 | // nil SIDIndexLabel 358 | _, err = l.serialize() 359 | assert.NotNil(t, err) 360 | 361 | // err serializing SIDIndexLabel 362 | l.SIDIndexLabel = &SIDIndexLabelLabel{ 363 | Label: 1 << 25, 364 | } 365 | _, err = l.serialize() 366 | assert.NotNil(t, err) 367 | } 368 | 369 | func TestLinkAttrLanAdjSID(t *testing.T) { 370 | l := &LinkAttrLanAdjSID{} 371 | 372 | // invalid len 373 | err := l.deserialize([]byte{}, 0) 374 | assert.NotNil(t, err) 375 | 376 | // invalid flags 377 | err = l.deserialize([]byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 0) 378 | assert.NotNil(t, err) 379 | 380 | // valid ospf id 381 | err = l.deserialize([]byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, LinkStateNlriOSPFv2ProtocolID) 382 | assert.Nil(t, err) 383 | 384 | // valid isis id 385 | err = l.deserialize([]byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, LinkStateNlriIsIsL1ProtocolID) 386 | assert.Nil(t, err) 387 | 388 | // err deserializing SIDIndexLabel 389 | err = l.deserialize([]byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, LinkStateNlriIsIsL1ProtocolID) 390 | assert.NotNil(t, err) 391 | 392 | // err serializing SIDIndexLabel 393 | l.SIDIndexLabel = &SIDIndexLabelLabel{ 394 | Label: 1 << 25, 395 | } 396 | _, err = l.serialize() 397 | assert.NotNil(t, err) 398 | 399 | // nil SIDIndexLabel 400 | l.SIDIndexLabel = nil 401 | _, err = l.serialize() 402 | assert.NotNil(t, err) 403 | 404 | // nil NeighborIDSystemID 405 | l.NeighborIDSystemID = nil 406 | _, err = l.serialize() 407 | assert.NotNil(t, err) 408 | 409 | // nil flags 410 | l.Flags = nil 411 | _, err = l.serialize() 412 | assert.NotNil(t, err) 413 | } 414 | 415 | func TestLinkAttrLanAdjSIDProtoSpecificID(t *testing.T) { 416 | o := &LinkAttrLanAdjSIDProtoSpecificIDOspf{} 417 | assert.Equal(t, o.Type(), LinkAttrLanAdjSIDProtoSpecificIDTypeOspf) 418 | 419 | // invalid len 420 | err := o.deserialize([]byte{}) 421 | assert.NotNil(t, err) 422 | 423 | // valid id 424 | err = o.deserialize([]byte{0, 0, 0, 1}) 425 | assert.Nil(t, err) 426 | 427 | b := o.serialize() 428 | assert.Equal(t, b, []byte{0, 0, 0, 1}) 429 | 430 | i := &LinkAttrLanAdjSIDProtoSpecificIDIsIs{} 431 | assert.Equal(t, i.Type(), LinkAttrLanAdjSIDProtoSpecificIDTypeIsIs) 432 | 433 | // invalid len 434 | err = i.deserialize([]byte{}) 435 | assert.NotNil(t, err) 436 | 437 | // valid id 438 | err = i.deserialize([]byte{0, 0, 0, 0, 0, 1}) 439 | assert.Nil(t, err) 440 | 441 | b = i.serialize() 442 | assert.Equal(t, b, []byte{0, 0, 0, 0, 0, 1}) 443 | } 444 | 445 | func TestLinkAttrAdjSID(t *testing.T) { 446 | l := &LinkAttrAdjSID{} 447 | 448 | // err deserializing flags 449 | err := l.deserialize([]byte{0, 0, 0, 0, 0, 0, 0}, LinkStateNlriDirectProtocolID) 450 | assert.NotNil(t, err) 451 | 452 | // err deserializing SIDIndexLabel 453 | err = l.deserialize([]byte{0, 0, 0, 0, 0, 0, 0, 0, 0}, LinkStateNlriOSPFv2ProtocolID) 454 | assert.NotNil(t, err) 455 | 456 | // missing SIDIndexLabel 457 | _, err = l.serialize() 458 | assert.NotNil(t, err) 459 | 460 | // missing flags 461 | l.Flags = nil 462 | l.SIDIndexLabel = &SIDIndexLabelOffset{ 463 | Offset: 2, 464 | } 465 | _, err = l.serialize() 466 | assert.NotNil(t, err) 467 | 468 | // err serializing SIDIndexLabel 469 | l.Flags = &LinkAttrAdjSIDFlagsOspf{} 470 | l.SIDIndexLabel = &SIDIndexLabelLabel{ 471 | Label: 1 << 25, 472 | } 473 | _, err = l.serialize() 474 | assert.NotNil(t, err) 475 | } 476 | 477 | func TestNlriProtocolIs(t *testing.T) { 478 | for i := 1; i < 8; i++ { 479 | proto := LinkStateNlriProtocolID(i) 480 | 481 | b := nlriProtocolIsOspf(proto) 482 | if proto == LinkStateNlriOSPFv2ProtocolID || proto == LinkStateNlriOSPFv3ProtocolID { 483 | assert.True(t, b) 484 | } else { 485 | assert.False(t, b) 486 | } 487 | 488 | c := nlriProtocolIsIsIs(proto) 489 | if proto == LinkStateNlriIsIsL1ProtocolID || proto == LinkStateNlriIsIsL2ProtocolID { 490 | assert.True(t, c) 491 | } else { 492 | assert.False(t, c) 493 | } 494 | } 495 | 496 | } 497 | 498 | func TestLinkAttrAdjSIDFlags(t *testing.T) { 499 | i := &LinkAttrAdjSIDFlagsIsIs{} 500 | assert.Equal(t, i.Type(), LinkAttrAdjSIDFlagsTypeIsIs) 501 | 502 | i.deserialize(252) 503 | assert.True(t, i.AddressFamily) 504 | assert.True(t, i.Backup) 505 | assert.True(t, i.Value) 506 | assert.True(t, i.Local) 507 | assert.True(t, i.Set) 508 | assert.True(t, i.Persistent) 509 | assert.Equal(t, uint8(252), i.serialize()) 510 | 511 | o := &LinkAttrAdjSIDFlagsOspf{} 512 | assert.Equal(t, o.Type(), LinkAttrAdjSIDFlagsTypeOspf) 513 | 514 | o.deserialize(248) 515 | assert.True(t, o.Backup) 516 | assert.True(t, o.Value) 517 | assert.True(t, o.Local) 518 | assert.True(t, o.Group) 519 | assert.True(t, o.Persistent) 520 | assert.Equal(t, uint8(248), o.serialize()) 521 | 522 | // ospf flags 523 | _, err := deserializeLinkAttrAdjSIDFlags(252, LinkStateNlriOSPFv2ProtocolID) 524 | assert.Nil(t, err) 525 | 526 | // isis flags 527 | _, err = deserializeLinkAttrAdjSIDFlags(248, LinkStateNlriIsIsL1ProtocolID) 528 | assert.Nil(t, err) 529 | 530 | // invalid proto 531 | _, err = deserializeLinkAttrAdjSIDFlags(248, LinkStateNlriDirectProtocolID) 532 | assert.NotNil(t, err) 533 | } 534 | 535 | func TestSIDIndexLabel(t *testing.T) { 536 | l := &SIDIndexLabelLabel{} 537 | assert.Equal(t, l.Type(), SIDIndexLabelTypeLabel) 538 | 539 | // invalid len 540 | err := l.deserialize([]byte{}) 541 | assert.NotNil(t, err) 542 | 543 | // overflow 3 octets 544 | l.Label = 1 << 25 545 | _, err = l.serialize() 546 | assert.NotNil(t, err) 547 | 548 | // invalid len 549 | _, err = deserializeSIDIndexLabel([]byte{}) 550 | assert.NotNil(t, err) 551 | 552 | o := &SIDIndexLabelOffset{} 553 | assert.Equal(t, o.Type(), SIDIndexLabelTypeOffset) 554 | 555 | // invalid len 556 | err = o.deserialize([]byte{}) 557 | assert.NotNil(t, err) 558 | } 559 | 560 | func TestNodeAttrSRLocalBlock(t *testing.T) { 561 | lb := &NodeAttrSRLocalBlock{ 562 | RangeSIDLabel: []RangeSIDLabel{ 563 | RangeSIDLabel{ 564 | RangeSize: 2, 565 | }, 566 | }, 567 | } 568 | 569 | // err serializing RangeSIDLabel 570 | _, err := lb.serialize() 571 | assert.NotNil(t, err) 572 | 573 | // err deserializing RangeSIDLabel 574 | err = lb.deserialize([]byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}) 575 | assert.NotNil(t, err) 576 | } 577 | 578 | func TestNodeAttrSRAlgo(t *testing.T) { 579 | a := &NodeAttrSRAlgo{} 580 | 581 | // empty algos 582 | _, err := a.serialize() 583 | assert.NotNil(t, err) 584 | } 585 | 586 | func TestNodeAttSRCaps(t *testing.T) { 587 | caps := &NodeAttrSRCaps{ 588 | RangeSIDLabel: []RangeSIDLabel{ 589 | RangeSIDLabel{ 590 | RangeSize: 2, 591 | }, 592 | }, 593 | } 594 | 595 | // err serializing RangeSIDLabel 596 | _, err := caps.serialize() 597 | assert.NotNil(t, err) 598 | 599 | // err deserializing RangeSIDLabel 600 | err = caps.deserialize([]byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}) 601 | assert.NotNil(t, err) 602 | } 603 | 604 | func TestRangeSIDLabel(t *testing.T) { 605 | r := &RangeSIDLabel{} 606 | 607 | // missing SIDLabel 608 | _, err := r.serialize() 609 | assert.NotNil(t, err) 610 | 611 | // err serializing SIDLabel 612 | r.SIDLabel = &SIDLabelLabel{ 613 | Label: 1 << 25, 614 | } 615 | _, err = r.serialize() 616 | assert.NotNil(t, err) 617 | 618 | // overflow RangeSize 619 | r.RangeSize = 1 << 25 620 | _, err = r.serialize() 621 | assert.NotNil(t, err) 622 | 623 | // len < 10 624 | _, err = deserializeRangeSIDLabel([]byte{0}) 625 | assert.NotNil(t, err) 626 | 627 | // invalid sidLabelCode 628 | _, err = deserializeRangeSIDLabel([]byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0}) 629 | assert.NotNil(t, err) 630 | 631 | // invalid sidLabel len field 632 | _, err = deserializeRangeSIDLabel([]byte{0, 0, 0, 4, 137, 0, 100, 0, 0, 0}) 633 | assert.NotNil(t, err) 634 | 635 | // invalid sidLabel len 636 | _, err = deserializeRangeSIDLabel([]byte{0, 0, 0, 4, 137, 0, 5, 0, 0, 0, 0, 0}) 637 | assert.NotNil(t, err) 638 | } 639 | 640 | func TestSIDLabel(t *testing.T) { 641 | l := &SIDLabelLabel{} 642 | assert.Equal(t, l.Type(), SIDLabelTypeLabel) 643 | 644 | // invalid len 645 | err := l.deserialize([]byte{0}) 646 | assert.NotNil(t, err) 647 | 648 | // overflow 3 octets 649 | l.Label = 1 << 25 650 | _, err = l.serialize() 651 | assert.NotNil(t, err) 652 | 653 | s := &SIDLabelSID{} 654 | assert.Equal(t, s.Type(), SIDLabelTypeSID) 655 | 656 | // invalid len 657 | err = s.deserialize([]byte{0}) 658 | assert.NotNil(t, err) 659 | } 660 | 661 | func TestLinkStateNlriNode(t *testing.T) { 662 | n := &LinkStateNlriNode{} 663 | assert.Equal(t, n.Type(), LinkStateNlriNodeType) 664 | assert.Equal(t, n.Afi(), BgpLsAfi) 665 | assert.Equal(t, n.Safi(), BgpLsSafi) 666 | 667 | // invalid local node descriptors TLV 668 | err := n.deserialize([]byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}) 669 | assert.NotNil(t, err) 670 | 671 | // invalid local node descriptors length 672 | err = n.deserialize([]byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 100, 0, 0, 0, 0}) 673 | assert.NotNil(t, err) 674 | 675 | // err deserializing node descriptors 676 | err = n.deserialize([]byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 4, 0, 0, 0, 0}) 677 | assert.NotNil(t, err) 678 | } 679 | 680 | func TestLinkStateNlriLink(t *testing.T) { 681 | l := &LinkStateNlriLink{} 682 | assert.Equal(t, l.Type(), LinkStateNlriLinkType) 683 | assert.Equal(t, l.Afi(), BgpLsAfi) 684 | assert.Equal(t, l.Safi(), BgpLsSafi) 685 | 686 | // invalid local node descriptors TLV 687 | err := l.deserialize([]byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}) 688 | assert.NotNil(t, err) 689 | 690 | // invalid local node descriptors len 691 | err = l.deserialize([]byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 100, 0, 0, 0, 0}) 692 | assert.NotNil(t, err) 693 | 694 | // err deserializing node descriptors 695 | err = l.deserialize([]byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0}) 696 | assert.NotNil(t, err) 697 | 698 | // no remote node descriptors 699 | err = l.deserialize([]byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 8, 2, 0, 0, 4, 0, 0, 0, 1}) 700 | assert.NotNil(t, err) 701 | 702 | // invalid remote node descriptors tlv 703 | err = l.deserialize([]byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 8, 2, 0, 0, 4, 0, 0, 0, 1, 0, 0, 0, 0}) 704 | assert.NotNil(t, err) 705 | 706 | // invalid remote node descriptors len 707 | err = l.deserialize([]byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 8, 2, 0, 0, 4, 0, 0, 0, 1, 1, 1, 0, 100, 0}) 708 | assert.NotNil(t, err) 709 | 710 | // err deserializing remote node descriptors 711 | err = l.deserialize([]byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 8, 2, 0, 0, 4, 0, 0, 0, 1, 1, 1, 0, 1, 0}) 712 | assert.NotNil(t, err) 713 | 714 | // no link descriptors 715 | err = l.deserialize([]byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 8, 2, 0, 0, 4, 0, 0, 0, 1, 1, 1, 0, 8, 2, 0, 0, 4, 0, 0, 0, 1}) 716 | assert.Nil(t, err) 717 | 718 | // < 4 bytes for link descriptors 719 | err = l.deserialize([]byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 8, 2, 0, 0, 4, 0, 0, 0, 1, 1, 1, 0, 8, 2, 0, 0, 4, 0, 0, 0, 1, 0}) 720 | assert.NotNil(t, err) 721 | 722 | // err deserializing link descriptors 723 | err = l.deserialize([]byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 8, 2, 0, 0, 4, 0, 0, 0, 1, 1, 1, 0, 8, 2, 0, 0, 4, 0, 0, 0, 1, 0, 0, 0, 0}) 724 | assert.NotNil(t, err) 725 | 726 | // err serializing local node descriptors 727 | l.LocalNodeDescriptors = []NodeDescriptor{&NodeDescriptorBgpRouterID{}} 728 | _, err = l.serialize() 729 | assert.NotNil(t, err) 730 | 731 | // err serializing remote node descriptors 732 | l.LocalNodeDescriptors = nil 733 | l.RemoteNodeDescriptors = []NodeDescriptor{&NodeDescriptorBgpRouterID{}} 734 | _, err = l.serialize() 735 | assert.NotNil(t, err) 736 | 737 | // err serializing link descriptors 738 | l.RemoteNodeDescriptors = nil 739 | l.LinkDescriptors = []LinkDescriptor{&LinkDescriptorIPv4NeighborAddress{}} 740 | _, err = l.serialize() 741 | assert.NotNil(t, err) 742 | } 743 | 744 | func TestLinkStateNlriPrefix(t *testing.T) { 745 | p := &LinkStateNlriPrefix{} 746 | assert.Equal(t, p.Afi(), BgpLsAfi) 747 | assert.Equal(t, p.Safi(), BgpLsSafi) 748 | 749 | // invalid local node descriptors TLV 750 | err := p.deserialize([]byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}) 751 | assert.NotNil(t, err) 752 | 753 | // invalid local node descriptors len 754 | err = p.deserialize([]byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 100, 0}) 755 | assert.NotNil(t, err) 756 | 757 | // err deserializing node descriptors 758 | err = p.deserialize([]byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0}) 759 | assert.NotNil(t, err) 760 | 761 | // no prefix descriptors 762 | err = p.deserialize([]byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 8, 2, 0, 0, 4, 0, 0, 0, 1}) 763 | assert.Nil(t, err) 764 | 765 | // < 4 bytes following node descriptors 766 | err = p.deserialize([]byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 8, 2, 0, 0, 4, 0, 0, 0, 1, 0}) 767 | assert.NotNil(t, err) 768 | 769 | // err deserializing prefix descriptors 770 | err = p.deserialize([]byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 8, 2, 0, 0, 4, 0, 0, 0, 1, 0, 0, 0, 0}) 771 | assert.NotNil(t, err) 772 | 773 | // err serializing node descriptors 774 | p.LocalNodeDescriptors = []NodeDescriptor{&NodeDescriptorBgpRouterID{}} 775 | _, err = p.serialize(LinkStateNlriIPv4PrefixType) 776 | assert.NotNil(t, err) 777 | 778 | // err serializing prefix descriptors 779 | p.LocalNodeDescriptors = nil 780 | p.PrefixDescriptors = []PrefixDescriptor{&PrefixDescriptorIPReachabilityInfo{}} 781 | _, err = p.serialize(LinkStateNlriIPv4PrefixType) 782 | assert.NotNil(t, err) 783 | } 784 | 785 | func TestPathAttrMpUnreach(t *testing.T) { 786 | mp := &PathAttrMpUnreach{} 787 | assert.Equal(t, mp.Type(), PathAttrMpUnreachType) 788 | assert.Equal(t, mp.Flags(), PathAttrFlags{}) 789 | 790 | // invalid len 791 | err := mp.deserialize(PathAttrFlags{}, []byte{0, 0, 0, 10, 0}) 792 | assert.NotNil(t, err) 793 | 794 | // err serializing nlri 795 | mp.Nlri = []LinkStateNlri{ 796 | &LinkStateNlriNode{ 797 | LocalNodeDescriptors: []NodeDescriptor{ 798 | &NodeDescriptorBgpRouterID{}, 799 | }, 800 | }, 801 | } 802 | _, err = mp.serialize() 803 | assert.NotNil(t, err) 804 | 805 | // ext len flag 806 | mp.Nlri = nil 807 | for i := 0; i < 256; i++ { 808 | mp.Nlri = append(mp.Nlri, &LinkStateNlriNode{ 809 | LocalNodeDescriptors: []NodeDescriptor{ 810 | &NodeDescriptorBgpRouterID{ 811 | RouterID: net.ParseIP("1.1.1.1").To4(), 812 | }, 813 | }, 814 | }) 815 | } 816 | _, err = mp.serialize() 817 | assert.Nil(t, err) 818 | } 819 | 820 | func TestPathAttrMpReach(t *testing.T) { 821 | mp := &PathAttrMpReach{} 822 | assert.Equal(t, mp.Type(), PathAttrMpReachType) 823 | assert.Equal(t, mp.Flags(), PathAttrFlags{}) 824 | 825 | // invalid len 826 | err := mp.deserialize(PathAttrFlags{}, []byte{0, 0, 0, 10, 0}) 827 | assert.NotNil(t, err) 828 | 829 | // err deserializing nlri 830 | err = mp.deserialize(PathAttrFlags{}, []byte{0, 0, 0, 0, 0}) 831 | assert.NotNil(t, err) 832 | 833 | // err serializing nlri 834 | mp.Nlri = []LinkStateNlri{ 835 | &LinkStateNlriNode{ 836 | LocalNodeDescriptors: []NodeDescriptor{ 837 | &NodeDescriptorBgpRouterID{}, 838 | }, 839 | }, 840 | } 841 | _, err = mp.serialize() 842 | assert.NotNil(t, err) 843 | } 844 | 845 | func TestDeserializeLinkStateNlri(t *testing.T) { 846 | // invalid afi/safi 847 | _, err := deserializeLinkStateNlri(0, 0, []byte{}) 848 | assert.NotNil(t, err) 849 | 850 | // len < 4 851 | _, err = deserializeLinkStateNlri(BgpLsAfi, BgpLsSafi, []byte{0}) 852 | assert.NotNil(t, err) 853 | 854 | // invalid nlri len 855 | _, err = deserializeLinkStateNlri(BgpLsAfi, BgpLsSafi, []byte{0, 0, 0, 10, 0}) 856 | assert.NotNil(t, err) 857 | 858 | // err deserializing each link state nlri type 859 | for i := 1; i < 6; i++ { 860 | _, err = deserializeLinkStateNlri(BgpLsAfi, BgpLsSafi, []byte{0, uint8(i), 0, 0}) 861 | assert.NotNil(t, err) 862 | } 863 | } 864 | 865 | func TestPathAttrOrigin(t *testing.T) { 866 | cases := []struct { 867 | c OriginCode 868 | s string 869 | }{ 870 | {OriginCodeIGP, "igp"}, 871 | {OriginCodeEGP, "egp"}, 872 | {OriginCodeIncomplete, "incomplete"}, 873 | {OriginCode(3), "unknown"}, 874 | } 875 | 876 | for _, c := range cases { 877 | assert.Equal(t, c.c.String(), c.s) 878 | } 879 | 880 | o := &PathAttrOrigin{} 881 | assert.Equal(t, o.Type(), PathAttrOriginType) 882 | assert.Equal(t, o.Flags(), PathAttrFlags{}) 883 | 884 | // empty attr 885 | err := o.deserialize(PathAttrFlags{}, []byte{}) 886 | assert.NotNil(t, err) 887 | } 888 | 889 | func TestPathAttrAsPath(t *testing.T) { 890 | asp := &PathAttrAsPath{} 891 | assert.Equal(t, asp.Type(), PathAttrAsPathType) 892 | assert.Equal(t, asp.Flags(), PathAttrFlags{}) 893 | 894 | // extended len 895 | segments := make([]AsPathSegment, 0) 896 | for i := 1; i < 256; i++ { 897 | segments = append(segments, &AsPathSegmentSet{Set: []uint16{1}}) 898 | } 899 | asp.Segments = segments 900 | _, err := asp.serialize() 901 | assert.Nil(t, err) 902 | 903 | // empty attr 904 | err = asp.deserialize(PathAttrFlags{}, []byte{}) 905 | assert.Nil(t, err) 906 | 907 | // < 2 bytes 908 | err = asp.deserialize(PathAttrFlags{}, []byte{1}) 909 | assert.NotNil(t, err) 910 | 911 | // invalid segment len 912 | err = asp.deserialize(PathAttrFlags{}, []byte{0, 100, 0, 0}) 913 | assert.NotNil(t, err) 914 | 915 | // invalid segment type 916 | err = asp.deserialize(PathAttrFlags{}, []byte{0, 2, 0, 0, 0, 0}) 917 | assert.NotNil(t, err) 918 | 919 | // err deserialize sequence type 920 | err = asp.deserialize(PathAttrFlags{}, []byte{2, 0}) 921 | assert.NotNil(t, err) 922 | 923 | // err deserialize set type 924 | err = asp.deserialize(PathAttrFlags{}, []byte{1, 0}) 925 | assert.NotNil(t, err) 926 | 927 | // error serializing segments 928 | asp = &PathAttrAsPath{ 929 | Segments: []AsPathSegment{ 930 | &AsPathSegmentSet{}, 931 | &AsPathSegmentSequence{}, 932 | }, 933 | } 934 | _, err = asp.serialize() 935 | assert.NotNil(t, err) 936 | 937 | // segment tests 938 | seq := &AsPathSegmentSequence{} 939 | assert.Equal(t, seq.Type(), AsPathSegmentSequenceType) 940 | _, err = seq.serialize() 941 | assert.NotNil(t, err) 942 | set := &AsPathSegmentSet{} 943 | assert.Equal(t, set.Type(), AsPathSegmentSetType) 944 | _, err = seq.serialize() 945 | assert.NotNil(t, err) 946 | } 947 | 948 | func TestPathAttrLocalPref(t *testing.T) { 949 | lp := &PathAttrLocalPref{} 950 | assert.Equal(t, lp.Type(), PathAttrLocalPrefType) 951 | assert.Equal(t, lp.Flags(), PathAttrFlags{}) 952 | } 953 | 954 | func TestStringTLVSerialization(t *testing.T) { 955 | _, err := serializeBgpLsStringTLV(0, "test") 956 | assert.Nil(t, err) 957 | _, err = serializeBgpLsStringTLV(0, "") 958 | assert.NotNil(t, err) 959 | } 960 | 961 | func TestIPTlvSerialization(t *testing.T) { 962 | b, err := serializeBgpLsIPv4TLV(1, []byte{1, 1, 1, 1}) 963 | assert.Nil(t, err) 964 | assert.Equal(t, b, []byte{0, 1, 0, 4, 1, 1, 1, 1}) 965 | _, err = serializeBgpLsIPv4TLV(1, []byte{1, 1, 1, 1, 1}) 966 | assert.NotNil(t, err) 967 | 968 | b, err = serializeBgpLsIPv6TLV(1, []byte{1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}) 969 | assert.Nil(t, err) 970 | assert.Equal(t, b, []byte{0, 1, 0, 16, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}) 971 | _, err = serializeBgpLsIPv6TLV(1, []byte{1, 1, 1, 1, 1}) 972 | assert.NotNil(t, err) 973 | 974 | _, err = deserializeIPv4Addr([]byte{1, 1, 1, 1}) 975 | assert.Nil(t, err) 976 | _, err = deserializeIPv4Addr([]byte{1, 1, 1, 1, 1}) 977 | assert.NotNil(t, err) 978 | 979 | _, err = deserializeIPv6Addr([]byte{1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}) 980 | assert.Nil(t, err) 981 | _, err = deserializeIPv6Addr([]byte{1, 1, 1, 1, 1}) 982 | assert.NotNil(t, err) 983 | } 984 | 985 | func TestDeserializeLinkStateAttrs(t *testing.T) { 986 | // err on attr deserialization 987 | cases := []struct { 988 | a uint16 989 | b []byte 990 | }{ 991 | { 992 | uint16(NodeAttrCodeIsIsAreaID), 993 | []byte{0}, 994 | }, 995 | { 996 | uint16(NodeAttrCodeLocalIPv4RouterID), 997 | []byte{0}, 998 | }, 999 | { 1000 | uint16(NodeAttrCodeLocalIPv6RouterID), 1001 | []byte{0}, 1002 | }, 1003 | { 1004 | uint16(NodeAttrCodeMultiTopologyID), 1005 | []byte{0}, 1006 | }, 1007 | { 1008 | uint16(NodeAttrCodeNodeFlagBits), 1009 | []byte{0, 0}, 1010 | }, 1011 | { 1012 | uint16(NodeAttrCodeNodeName), 1013 | []byte{}, 1014 | }, 1015 | { 1016 | uint16(NodeAttrCodeOpaqueNodeAttr), 1017 | []byte{}, 1018 | }, 1019 | { 1020 | uint16(NodeAttrCodeSRCaps), 1021 | []byte{}, 1022 | }, 1023 | { 1024 | uint16(NodeAttrCodeSRAlgo), 1025 | []byte{}, 1026 | }, 1027 | { 1028 | uint16(NodeAttrCodeSRLocalBlock), 1029 | []byte{}, 1030 | }, 1031 | { 1032 | uint16(NodeAttrCodeSRMSPref), 1033 | []byte{}, 1034 | }, 1035 | { 1036 | uint16(LinkAttrCodeAdminGroup), 1037 | []byte{0, 0}, 1038 | }, 1039 | { 1040 | uint16(LinkAttrCodeIgpMetric), 1041 | []byte{0, 0, 0, 0}, 1042 | }, 1043 | { 1044 | uint16(LinkAttrCodeLinkName), 1045 | []byte{}, 1046 | }, 1047 | { 1048 | uint16(LinkAttrCodeLinkProtectionType), 1049 | []byte{0, 0, 0, 0}, 1050 | }, 1051 | { 1052 | uint16(LinkAttrCodeMaxLinkBandwidth), 1053 | []byte{0, 0, 0}, 1054 | }, 1055 | { 1056 | uint16(LinkAttrCodeMaxReservableLinkBandwidth), 1057 | []byte{0, 0, 0}, 1058 | }, 1059 | { 1060 | uint16(LinkAttrCodeMplsProtocolMask), 1061 | []byte{0, 0, 0}, 1062 | }, 1063 | { 1064 | uint16(LinkAttrCodeRemoteIPv4RouterID), 1065 | []byte{0, 0, 0}, 1066 | }, 1067 | { 1068 | uint16(LinkAttrCodeRemoteIPv6RouterID), 1069 | []byte{0, 0, 0}, 1070 | }, 1071 | { 1072 | uint16(LinkAttrCodeSharedRiskLinkGroup), 1073 | []byte{0, 0, 0}, 1074 | }, 1075 | { 1076 | uint16(LinkAttrCodeOpaqueLinkAttr), 1077 | []byte{}, 1078 | }, 1079 | { 1080 | uint16(LinkAttrCodeTEDefaultMetric), 1081 | []byte{0, 0, 0}, 1082 | }, 1083 | { 1084 | uint16(LinkAttrCodeUnreservedBandwidth), 1085 | []byte{0, 0, 0}, 1086 | }, 1087 | { 1088 | uint16(LinkAttrCodePeerNodeSID), 1089 | []byte{}, 1090 | }, 1091 | { 1092 | uint16(LinkAttrCodePeerAdjSID), 1093 | []byte{}, 1094 | }, 1095 | { 1096 | uint16(LinkAttrCodePeerSetSID), 1097 | []byte{}, 1098 | }, 1099 | { 1100 | uint16(LinkAttrCodeAdjSID), 1101 | []byte{}, 1102 | }, 1103 | { 1104 | uint16(LinkAttrCodeLanAdjSID), 1105 | []byte{}, 1106 | }, 1107 | { 1108 | uint16(LinkAttrCodeUniLinkDelay), 1109 | []byte{}, 1110 | }, 1111 | { 1112 | uint16(LinkAttrCodeMinMaxUniLinkDelay), 1113 | []byte{}, 1114 | }, 1115 | { 1116 | uint16(LinkAttrCodeUniDelayVariation), 1117 | []byte{}, 1118 | }, 1119 | { 1120 | uint16(LinkAttrCodeUniPacketLoss), 1121 | []byte{}, 1122 | }, 1123 | { 1124 | uint16(LinkAttrCodeUniResidualBandwidth), 1125 | []byte{}, 1126 | }, 1127 | { 1128 | uint16(LinkAttrCodeUniAvailableBandwidth), 1129 | []byte{}, 1130 | }, 1131 | { 1132 | uint16(LinkAttrCodeUniBandwidthUtil), 1133 | []byte{}, 1134 | }, 1135 | { 1136 | uint16(LinkAttrCodeL2BundleMember), 1137 | []byte{}, 1138 | }, 1139 | { 1140 | uint16(PrefixAttrCodeIgpExtendedRouteTag), 1141 | []byte{0, 0, 0}, 1142 | }, 1143 | { 1144 | uint16(PrefixAttrCodeIgpFlags), 1145 | []byte{0, 0, 0}, 1146 | }, 1147 | { 1148 | uint16(PrefixAttrCodeIgpRouteTag), 1149 | []byte{0, 0, 0}, 1150 | }, 1151 | { 1152 | uint16(PrefixAttrCodeOpaquePrefixAttribute), 1153 | []byte{}, 1154 | }, 1155 | { 1156 | uint16(PrefixAttrCodeOspfForwardingAddress), 1157 | []byte{0, 0, 0}, 1158 | }, 1159 | { 1160 | uint16(PrefixAttrCodePrefixMetric), 1161 | []byte{0, 0, 0}, 1162 | }, 1163 | { 1164 | uint16(PrefixAttrCodePrefixSID), 1165 | []byte{}, 1166 | }, 1167 | { 1168 | uint16(PrefixAttrCodeRange), 1169 | []byte{}, 1170 | }, 1171 | { 1172 | uint16(PrefixAttrCodeFlags), 1173 | []byte{}, 1174 | }, 1175 | { 1176 | uint16(PrefixAttrCodeSourceRouterID), 1177 | []byte{}, 1178 | }, 1179 | { 1180 | uint16(0), 1181 | []byte{0, 0, 0}, 1182 | }, 1183 | } 1184 | 1185 | for _, c := range cases { 1186 | b := make([]byte, 4) 1187 | binary.BigEndian.PutUint16(b[:2], uint16(c.a)) 1188 | binary.BigEndian.PutUint16(b[2:], uint16(len(c.b))) 1189 | b = append(b, c.b...) 1190 | _, _, _, err := deserializeLinkStateAttrs(b, 0) 1191 | assert.NotNil(t, err) 1192 | } 1193 | 1194 | // cases for PrefixAttrFlags with varying nlri protocol 1195 | protos := []LinkStateNlriProtocolID{ 1196 | LinkStateNlriOSPFv2ProtocolID, LinkStateNlriOSPFv3ProtocolID, 1197 | LinkStateNlriIsIsL1ProtocolID, LinkStateNlriIsIsL2ProtocolID, 1198 | } 1199 | for _, p := range protos { 1200 | b := make([]byte, 4) 1201 | binary.BigEndian.PutUint16(b, uint16(PrefixAttrCodeFlags)) 1202 | binary.BigEndian.PutUint16(b[2:], uint16(0)) 1203 | _, _, _, err := deserializeLinkStateAttrs(b, p) 1204 | assert.NotNil(t, err) 1205 | } 1206 | } 1207 | 1208 | func TestPathAttrLinkState(t *testing.T) { 1209 | ls := &PathAttrLinkState{} 1210 | assert.Equal(t, ls.Flags(), PathAttrFlags{}) 1211 | assert.Equal(t, ls.Type(), PathAttrLinkStateType) 1212 | err := ls.deserialize(PathAttrFlags{}, []byte{}, 0) 1213 | assert.Nil(t, err) 1214 | 1215 | // 0 > len < 4 1216 | err = ls.deserialize(PathAttrFlags{}, []byte{0}, 0) 1217 | assert.NotNil(t, err) 1218 | 1219 | // invalid attr len 1220 | err = ls.deserialize(PathAttrFlags{}, []byte{0, 0, 0, 100, 0}, 0) 1221 | assert.NotNil(t, err) 1222 | 1223 | // node attrs err on serialization 1224 | ls = &PathAttrLinkState{ 1225 | NodeAttrs: []NodeAttr{ 1226 | &NodeAttrLocalIPv4RouterID{ 1227 | Address: []byte{0}, 1228 | }, 1229 | }, 1230 | } 1231 | _, err = ls.serialize() 1232 | assert.NotNil(t, err) 1233 | 1234 | // link attrs err on serialization 1235 | ls = &PathAttrLinkState{ 1236 | LinkAttrs: []LinkAttr{ 1237 | &LinkAttrRemoteIPv4RouterID{ 1238 | Address: []byte{0}, 1239 | }, 1240 | }, 1241 | } 1242 | _, err = ls.serialize() 1243 | assert.NotNil(t, err) 1244 | 1245 | // prefix attrs err on serialization 1246 | ls = &PathAttrLinkState{ 1247 | PrefixAttrs: []PrefixAttr{ 1248 | &PrefixAttrOspfForwardingAddress{ 1249 | Address: []byte{0}, 1250 | }, 1251 | }, 1252 | } 1253 | _, err = ls.serialize() 1254 | assert.NotNil(t, err) 1255 | } 1256 | 1257 | func TestPathAttrFlags(t *testing.T) { 1258 | cases := []struct { 1259 | f PathAttrFlags 1260 | val uint8 1261 | }{ 1262 | { 1263 | PathAttrFlags{ 1264 | Optional: true, 1265 | }, 1266 | 128, 1267 | }, 1268 | { 1269 | PathAttrFlags{ 1270 | Transitive: true, 1271 | }, 1272 | 64, 1273 | }, 1274 | { 1275 | PathAttrFlags{ 1276 | Partial: true, 1277 | }, 1278 | 32, 1279 | }, 1280 | { 1281 | PathAttrFlags{ 1282 | ExtendedLength: true, 1283 | }, 1284 | 16, 1285 | }, 1286 | } 1287 | 1288 | for _, c := range cases { 1289 | f := pathAttrFlagsFromByte(c.val) 1290 | assert.Equal(t, f, c.f) 1291 | b := c.f.serialize() 1292 | assert.Equal(t, b, c.val) 1293 | } 1294 | } 1295 | 1296 | func TestValidatePathAttrFlags(t *testing.T) { 1297 | cases := []struct { 1298 | f PathAttrFlags 1299 | cat pathAttrCategory 1300 | err bool 1301 | }{ 1302 | { 1303 | PathAttrFlags{ 1304 | Transitive: true, 1305 | }, 1306 | pathAttrCatWellKnownMandatory, 1307 | false, 1308 | }, 1309 | { 1310 | PathAttrFlags{}, 1311 | pathAttrCatWellKnownDiscretionary, 1312 | false, 1313 | }, 1314 | { 1315 | PathAttrFlags{ 1316 | Optional: true, 1317 | Transitive: true, 1318 | }, 1319 | pathAttrCatOptionalTransitive, 1320 | false, 1321 | }, 1322 | } 1323 | 1324 | for _, c := range cases { 1325 | err := validatePathAttrFlags(c.f, c.cat) 1326 | if c.err { 1327 | assert.NotNil(t, err) 1328 | } else { 1329 | assert.Nil(t, err) 1330 | } 1331 | } 1332 | } 1333 | 1334 | func TestDeserializePathAttrs(t *testing.T) { 1335 | // bytes < attrLen 1336 | b := make([]byte, 4) 1337 | b[2] = uint8(100) 1338 | _, err := deserializePathAttrs(b) 1339 | assert.NotNil(t, err) 1340 | 1341 | // origin errors 1342 | o := &PathAttrOrigin{ 1343 | Origin: OriginCodeEGP, 1344 | } 1345 | b, err = o.serialize() 1346 | if err != nil { 1347 | t.Fatal(err) 1348 | } 1349 | // bad origin code 1350 | b[3] = 3 1351 | _, err = deserializePathAttrs(b) 1352 | assert.NotNil(t, err) 1353 | // set to valid origin code 1354 | b[3] = 2 1355 | // set flags to invalid value 1356 | b[0] = 0 1357 | _, err = deserializePathAttrs(b) 1358 | assert.NotNil(t, err) 1359 | 1360 | cases := []struct { 1361 | a PathAttr 1362 | invalidFlags uint8 1363 | bytesToRemove int 1364 | }{ 1365 | { 1366 | &PathAttrAsPath{ 1367 | Segments: []AsPathSegment{ 1368 | &AsPathSegmentSequence{ 1369 | Sequence: []uint16{1}, 1370 | }, 1371 | }, 1372 | }, 1373 | 0, 1, 1374 | }, 1375 | { 1376 | &PathAttrLocalPref{ 1377 | Preference: 100, 1378 | }, 1379 | 128, 1, 1380 | }, 1381 | { 1382 | &PathAttrMpReach{}, 1383 | 0, 1384 | 3, 1385 | }, 1386 | { 1387 | &PathAttrMpUnreach{}, 1388 | 0, 1389 | 3, 1390 | }, 1391 | } 1392 | 1393 | for _, c := range cases { 1394 | b, err = c.a.serialize() 1395 | if err != nil { 1396 | t.Fatal(err) 1397 | } 1398 | b = b[:len(b)-c.bytesToRemove] 1399 | b[2] = uint8(len(b) - 3) 1400 | _, err = deserializePathAttrs(b) 1401 | assert.NotNil(t, err) 1402 | b[0] = c.invalidFlags 1403 | _, err = deserializePathAttrs(b) 1404 | assert.NotNil(t, err) 1405 | } 1406 | 1407 | // link state errors 1408 | ls := &PathAttrLinkState{} 1409 | b, err = ls.serialize() 1410 | if err != nil { 1411 | t.Fatal(err) 1412 | } 1413 | b = append(b, 0) 1414 | b[2] = 1 1415 | _, err = deserializePathAttrs(b) 1416 | assert.NotNil(t, err) 1417 | b[0] = 0 1418 | _, err = deserializePathAttrs(b) 1419 | assert.NotNil(t, err) 1420 | } 1421 | 1422 | func TestUpdateSerialization(t *testing.T) { 1423 | // path attr serialization error 1424 | u := &UpdateMessage{ 1425 | PathAttrs: []PathAttr{ 1426 | &PathAttrOrigin{ 1427 | Origin: OriginCode(3), 1428 | }, 1429 | }, 1430 | } 1431 | _, err := u.serialize() 1432 | assert.NotNil(t, err) 1433 | 1434 | // len < 4 1435 | err = u.deserialize([]byte{0}) 1436 | assert.NotNil(t, err) 1437 | 1438 | // withdrawn routes invalid len 1439 | u = &UpdateMessage{} 1440 | b, err := u.serialize() 1441 | if err != nil { 1442 | t.Fatal(err) 1443 | } 1444 | binary.BigEndian.PutUint16(b[0:2], uint16(100)) 1445 | err = u.deserialize(b) 1446 | assert.NotNil(t, err) 1447 | 1448 | // path attr invalid len 1449 | binary.BigEndian.PutUint16(b[0:2], uint16(0)) 1450 | binary.BigEndian.PutUint16(b[2:4], uint16(200)) 1451 | err = u.deserialize(b) 1452 | assert.NotNil(t, err) 1453 | } 1454 | 1455 | func TestNodeAttrs(t *testing.T) { 1456 | attrs := []NodeAttr{ 1457 | &NodeAttrMultiTopologyID{ 1458 | IDs: []uint16{1, 2, 3}, 1459 | }, 1460 | &NodeAttrNodeFlagBits{ 1461 | Overload: true, 1462 | }, 1463 | &NodeAttrIsIsAreaID{ 1464 | AreaID: uint32(1), 1465 | }, 1466 | &NodeAttrLocalIPv4RouterID{ 1467 | Address: net.ParseIP("1.1.1.1").To4(), 1468 | }, 1469 | &NodeAttrLocalIPv6RouterID{ 1470 | Address: net.ParseIP("2601::").To16(), 1471 | }, 1472 | } 1473 | 1474 | for _, a := range attrs { 1475 | b, err := a.serialize() 1476 | assert.Nil(t, err) 1477 | b = append(b, uint8(0)) 1478 | err = a.deserialize(b) 1479 | assert.NotNil(t, err) 1480 | } 1481 | 1482 | n := &NodeAttrOpaqueNodeAttr{} 1483 | _, err := n.serialize() 1484 | assert.NotNil(t, err) 1485 | } 1486 | 1487 | func TestDeserializeLinkDescriptors(t *testing.T) { 1488 | // len < 4 1489 | _, err := deserializeLinkDescriptors(0, []byte{}) 1490 | assert.NotNil(t, err) 1491 | 1492 | // invalid descriptor len 1493 | _, err = deserializeLinkDescriptors(0, []byte{0, 0, 0, 10, 0}) 1494 | assert.NotNil(t, err) 1495 | 1496 | // err deserializing link ids 1497 | _, err = deserializeLinkDescriptors(0, []byte{1, 2, 0, 0}) 1498 | assert.NotNil(t, err) 1499 | 1500 | // err deserializing ipv4 int address 1501 | _, err = deserializeLinkDescriptors(0, []byte{1, 3, 0, 0}) 1502 | assert.NotNil(t, err) 1503 | 1504 | // err deserializing ipv4 neighbor address 1505 | _, err = deserializeLinkDescriptors(0, []byte{1, 4, 0, 0}) 1506 | assert.NotNil(t, err) 1507 | 1508 | // err deserializing ipv6 int address 1509 | _, err = deserializeLinkDescriptors(0, []byte{1, 5, 0, 0}) 1510 | assert.NotNil(t, err) 1511 | 1512 | // err deserializing ipv6 neighbor address 1513 | _, err = deserializeLinkDescriptors(0, []byte{1, 6, 0, 0}) 1514 | assert.NotNil(t, err) 1515 | 1516 | // err deserializing multi topo ids 1517 | _, err = deserializeLinkDescriptors(0, []byte{1, 7, 0, 0}) 1518 | assert.NotNil(t, err) 1519 | 1520 | // invalid link descriptor code 1521 | _, err = deserializeLinkDescriptors(0, []byte{0, 0, 0, 0}) 1522 | assert.NotNil(t, err) 1523 | } 1524 | 1525 | func TestDeserializePrefixDescriptors(t *testing.T) { 1526 | // len < 4 1527 | _, err := deserializePrefixDescriptors(0, []byte{}) 1528 | assert.NotNil(t, err) 1529 | 1530 | // invalid descriptor len 1531 | _, err = deserializePrefixDescriptors(0, []byte{0, 0, 0, 10, 0}) 1532 | assert.NotNil(t, err) 1533 | 1534 | // err deserializing multi topo id 1535 | _, err = deserializePrefixDescriptors(0, []byte{1, 7, 0, 0}) 1536 | assert.NotNil(t, err) 1537 | 1538 | // err deserializing ospf route type 1539 | _, err = deserializePrefixDescriptors(0, []byte{1, 8, 0, 0}) 1540 | assert.NotNil(t, err) 1541 | 1542 | // err deserializing ip reachability info 1543 | _, err = deserializePrefixDescriptors(0, []byte{1, 9, 0, 0}) 1544 | assert.NotNil(t, err) 1545 | 1546 | // invalid prefix descriptor code 1547 | _, err = deserializePrefixDescriptors(0, []byte{0, 0, 0, 0}) 1548 | assert.NotNil(t, err) 1549 | } 1550 | 1551 | func TestPrefixDescriptors(t *testing.T) { 1552 | descriptors := []PrefixDescriptor{ 1553 | &PrefixDescriptorIPReachabilityInfo{ 1554 | PrefixLength: uint8(32), 1555 | Prefix: net.ParseIP("1.2.3.4").To4(), 1556 | }, 1557 | &PrefixDescriptorIPReachabilityInfo{ 1558 | PrefixLength: uint8(128), 1559 | Prefix: net.ParseIP("2601::").To16(), 1560 | }, 1561 | &PrefixDescriptorMultiTopologyID{ 1562 | IDs: []uint16{0, 1, 2}, 1563 | }, 1564 | &PrefixDescriptorOspfRouteType{ 1565 | RouteType: OspfRouteTypeExternal1, 1566 | }, 1567 | } 1568 | 1569 | for _, d := range descriptors { 1570 | b, err := d.serialize() 1571 | assert.Nil(t, err) 1572 | b = append(b, uint8(0)) 1573 | err = d.deserialize(b) 1574 | assert.NotNil(t, err) 1575 | } 1576 | 1577 | // invalid addr 1578 | r := &PrefixDescriptorIPReachabilityInfo{} 1579 | _, err := r.serialize() 1580 | assert.NotNil(t, err) 1581 | 1582 | // invalid route type 1583 | o := &PrefixDescriptorOspfRouteType{} 1584 | err = o.deserialize([]byte{0}) 1585 | assert.NotNil(t, err) 1586 | } 1587 | 1588 | func TestLinkDescriptors(t *testing.T) { 1589 | descriptors := []LinkDescriptor{ 1590 | &LinkDescriptorLinkIDs{ 1591 | LocalID: uint32(2), 1592 | RemoteID: uint32(3), 1593 | }, 1594 | &LinkDescriptorIPv4InterfaceAddress{ 1595 | Address: net.ParseIP("1.1.1.1").To4(), 1596 | }, 1597 | &LinkDescriptorIPv4NeighborAddress{ 1598 | Address: net.ParseIP("2.2.2.2").To4(), 1599 | }, 1600 | &LinkDescriptorIPv6InterfaceAddress{ 1601 | Address: net.ParseIP("2601::").To16(), 1602 | }, 1603 | &LinkDescriptorIPv6NeighborAddress{ 1604 | Address: net.ParseIP("2601::").To16(), 1605 | }, 1606 | &LinkDescriptorMultiTopologyID{ 1607 | IDs: []uint16{0, 1, 2}, 1608 | }, 1609 | } 1610 | 1611 | for _, d := range descriptors { 1612 | b, err := d.serialize() 1613 | assert.Nil(t, err) 1614 | b = append(b, uint8(0)) 1615 | err = d.deserialize(b) 1616 | assert.NotNil(t, err) 1617 | } 1618 | } 1619 | 1620 | func TestDeserializeNodeDescriptors(t *testing.T) { 1621 | // too short 1622 | _, err := deserializeNodeDescriptors(0, []byte{0}) 1623 | assert.NotNil(t, err) 1624 | 1625 | // invalid descriptor len 1626 | _, err = deserializeNodeDescriptors(0, []byte{0, 0, 0, 10, 0}) 1627 | assert.NotNil(t, err) 1628 | 1629 | // err deserializing node descriptors 1630 | for i := 512; i < 519; i++ { 1631 | b := make([]byte, 2) 1632 | binary.BigEndian.PutUint16(b, uint16(i)) 1633 | _, err = deserializeNodeDescriptors(0, append(b, []byte{0, 0}...)) 1634 | assert.NotNil(t, err) 1635 | } 1636 | 1637 | // err igp router id is-is 1638 | _, err = deserializeNodeDescriptors(LinkStateNlriIsIsL1ProtocolID, []byte{2, 3, 0, 0}) 1639 | assert.NotNil(t, err) 1640 | 1641 | // err igp router id ospf 1642 | _, err = deserializeNodeDescriptors(LinkStateNlriOSPFv2ProtocolID, []byte{2, 3, 0, 0}) 1643 | assert.NotNil(t, err) 1644 | } 1645 | 1646 | func TestNodeDescriptors(t *testing.T) { 1647 | descriptors := []NodeDescriptor{ 1648 | &NodeDescriptorASN{ 1649 | ASN: uint32(64512), 1650 | }, 1651 | &NodeDescriptorBgpLsID{ 1652 | ID: uint32(1), 1653 | }, 1654 | &NodeDescriptorOspfAreaID{ 1655 | ID: uint32(1), 1656 | }, 1657 | &NodeDescriptorIgpRouterIDIsIsNonPseudo{ 1658 | IsoNodeID: uint64(1), 1659 | }, 1660 | &NodeDescriptorIgpRouterIDIsIsPseudo{ 1661 | IsoNodeID: uint64(1), 1662 | PsnID: uint8(1), 1663 | }, 1664 | &NodeDescriptorIgpRouterIDOspfNonPseudo{ 1665 | RouterID: net.ParseIP("1.1.1.1").To4(), 1666 | }, 1667 | &NodeDescriptorIgpRouterIDOspfPseudo{ 1668 | DrRouterID: net.ParseIP("1.1.1.1").To4(), 1669 | DrInterfaceToLAN: net.ParseIP("2.2.2.2").To4(), 1670 | }, 1671 | &NodeDescriptorBgpRouterID{ 1672 | RouterID: net.ParseIP("1.1.1.1").To4(), 1673 | }, 1674 | &NodeDescriptorMemberASN{ 1675 | ASN: uint32(64512), 1676 | }, 1677 | } 1678 | 1679 | for _, d := range descriptors { 1680 | b, err := d.serialize() 1681 | assert.Nil(t, err) 1682 | b = append(b, uint8(0)) 1683 | err = d.deserialize(b) 1684 | assert.NotNil(t, err) 1685 | } 1686 | 1687 | // invalid router id 1688 | d := &NodeDescriptorIgpRouterIDOspfPseudo{} 1689 | _, err := d.serialize() 1690 | assert.NotNil(t, err) 1691 | 1692 | // invalid dr interface to lan 1693 | d = &NodeDescriptorIgpRouterIDOspfPseudo{DrRouterID: net.ParseIP("1.1.1.1").To4()} 1694 | _, err = d.serialize() 1695 | assert.NotNil(t, err) 1696 | } 1697 | 1698 | func TestUpdateMessage(t *testing.T) { 1699 | var adminGroup [32]bool 1700 | adminGroup[31] = true 1701 | var unreservedBW [8]float32 1702 | unreservedBW[0] = 10000 1703 | 1704 | attrs := []PathAttr{ 1705 | &PathAttrMpUnreach{ 1706 | Afi: BgpLsAfi, 1707 | Safi: BgpLsSafi, 1708 | Nlri: []LinkStateNlri{ 1709 | &LinkStateNlriNode{ 1710 | ProtocolID: LinkStateNlriOSPFv2ProtocolID, 1711 | ID: uint64(56), 1712 | LocalNodeDescriptors: []NodeDescriptor{ 1713 | &NodeDescriptorASN{ 1714 | ASN: uint32(64512), 1715 | }, 1716 | }, 1717 | }, 1718 | }, 1719 | }, 1720 | &PathAttrMpReach{ 1721 | Afi: BgpLsAfi, 1722 | Safi: BgpLsSafi, 1723 | Nlri: []LinkStateNlri{ 1724 | &LinkStateNlriNode{ 1725 | ProtocolID: LinkStateNlriIsIsL1ProtocolID, 1726 | ID: uint64(55), 1727 | LocalNodeDescriptors: []NodeDescriptor{ 1728 | &NodeDescriptorASN{ 1729 | ASN: uint32(64512), 1730 | }, 1731 | &NodeDescriptorBgpLsID{ 1732 | ID: uint32(64512), 1733 | }, 1734 | &NodeDescriptorOspfAreaID{ 1735 | ID: uint32(1), 1736 | }, 1737 | &NodeDescriptorIgpRouterIDIsIsNonPseudo{ 1738 | IsoNodeID: uint64(2), 1739 | }, 1740 | &NodeDescriptorIgpRouterIDIsIsPseudo{ 1741 | IsoNodeID: uint64(3), 1742 | PsnID: uint8(4), 1743 | }, 1744 | &NodeDescriptorBgpRouterID{ 1745 | RouterID: net.ParseIP("172.16.1.1").To4(), 1746 | }, 1747 | &NodeDescriptorMemberASN{ 1748 | ASN: uint32(64512), 1749 | }, 1750 | }, 1751 | }, 1752 | &LinkStateNlriNode{ 1753 | ProtocolID: LinkStateNlriOSPFv2ProtocolID, 1754 | ID: uint64(56), 1755 | LocalNodeDescriptors: []NodeDescriptor{ 1756 | &NodeDescriptorIgpRouterIDOspfNonPseudo{ 1757 | RouterID: net.ParseIP("172.16.1.201").To4(), 1758 | }, 1759 | &NodeDescriptorIgpRouterIDOspfPseudo{ 1760 | DrRouterID: net.ParseIP("172.16.1.202").To4(), 1761 | DrInterfaceToLAN: net.ParseIP("172.16.1.203").To4(), 1762 | }, 1763 | }, 1764 | }, 1765 | &LinkStateNlriLink{ 1766 | ProtocolID: LinkStateNlriOSPFv2ProtocolID, 1767 | ID: uint64(57), 1768 | LocalNodeDescriptors: []NodeDescriptor{ 1769 | &NodeDescriptorASN{ 1770 | ASN: uint32(64512), 1771 | }, 1772 | }, 1773 | RemoteNodeDescriptors: []NodeDescriptor{ 1774 | &NodeDescriptorASN{ 1775 | ASN: uint32(64512), 1776 | }, 1777 | }, 1778 | LinkDescriptors: []LinkDescriptor{ 1779 | &LinkDescriptorLinkIDs{ 1780 | LocalID: uint32(5), 1781 | RemoteID: uint32(6), 1782 | }, 1783 | &LinkDescriptorIPv4InterfaceAddress{ 1784 | Address: net.ParseIP("172.16.1.1").To4(), 1785 | }, 1786 | &LinkDescriptorIPv4NeighborAddress{ 1787 | Address: net.ParseIP("172.16.1.2").To4(), 1788 | }, 1789 | &LinkDescriptorIPv6InterfaceAddress{ 1790 | Address: net.ParseIP("2601::1").To16(), 1791 | }, 1792 | &LinkDescriptorIPv6NeighborAddress{ 1793 | Address: net.ParseIP("2601::2").To16(), 1794 | }, 1795 | &LinkDescriptorMultiTopologyID{ 1796 | IDs: []uint16{1, 2, 3, 4}, 1797 | }, 1798 | }, 1799 | }, 1800 | &LinkStateNlriIPv4Prefix{ 1801 | LinkStateNlriPrefix: LinkStateNlriPrefix{ 1802 | ProtocolID: LinkStateNlriOSPFv2ProtocolID, 1803 | ID: uint64(58), 1804 | LocalNodeDescriptors: []NodeDescriptor{ 1805 | &NodeDescriptorASN{ 1806 | ASN: uint32(64512), 1807 | }, 1808 | }, 1809 | PrefixDescriptors: []PrefixDescriptor{ 1810 | &PrefixDescriptorIPReachabilityInfo{ 1811 | Prefix: net.ParseIP("172.16.1.4").To4(), 1812 | PrefixLength: uint8(32), 1813 | }, 1814 | &PrefixDescriptorMultiTopologyID{ 1815 | IDs: []uint16{10, 11, 12, 13}, 1816 | }, 1817 | &PrefixDescriptorOspfRouteType{ 1818 | RouteType: OspfRouteTypeExternal1, 1819 | }, 1820 | }, 1821 | }, 1822 | }, 1823 | &LinkStateNlriIPv6Prefix{ 1824 | LinkStateNlriPrefix: LinkStateNlriPrefix{ 1825 | ProtocolID: LinkStateNlriOSPFv2ProtocolID, 1826 | ID: uint64(58), 1827 | LocalNodeDescriptors: []NodeDescriptor{ 1828 | &NodeDescriptorASN{ 1829 | ASN: uint32(64512), 1830 | }, 1831 | }, 1832 | PrefixDescriptors: []PrefixDescriptor{ 1833 | &PrefixDescriptorIPReachabilityInfo{ 1834 | Prefix: net.ParseIP("2601::").To16(), 1835 | PrefixLength: uint8(32), 1836 | }, 1837 | &PrefixDescriptorMultiTopologyID{ 1838 | IDs: []uint16{10, 11, 12, 13}, 1839 | }, 1840 | &PrefixDescriptorOspfRouteType{ 1841 | RouteType: OspfRouteTypeExternal1, 1842 | }, 1843 | }, 1844 | }, 1845 | }, 1846 | }, 1847 | }, 1848 | &PathAttrOrigin{ 1849 | Origin: OriginCodeIGP, 1850 | }, 1851 | &PathAttrAsPath{ 1852 | Segments: []AsPathSegment{ 1853 | &AsPathSegmentSequence{ 1854 | Sequence: []uint16{64512}, 1855 | }, 1856 | &AsPathSegmentSet{ 1857 | Set: []uint16{64512}, 1858 | }, 1859 | }}, 1860 | &PathAttrLocalPref{ 1861 | Preference: uint32(200), 1862 | }, 1863 | &PathAttrLinkState{ 1864 | NodeAttrs: []NodeAttr{ 1865 | &NodeAttrNodeFlagBits{ 1866 | Overload: true, 1867 | Attached: true, 1868 | External: true, 1869 | ABR: true, 1870 | Router: true, 1871 | V6: true, 1872 | }, 1873 | &NodeAttrOpaqueNodeAttr{ 1874 | Data: []byte{0, 1, 2, 3}, 1875 | }, 1876 | &NodeAttrNodeName{ 1877 | Name: "test", 1878 | }, 1879 | &NodeAttrIsIsAreaID{ 1880 | AreaID: uint32(64512), 1881 | }, 1882 | &NodeAttrLocalIPv4RouterID{ 1883 | Address: net.ParseIP("172.16.1.201").To4(), 1884 | }, 1885 | &NodeAttrLocalIPv6RouterID{ 1886 | Address: net.ParseIP("2601::1"), 1887 | }, 1888 | &NodeAttrMultiTopologyID{ 1889 | IDs: []uint16{1, 2, 3, 4}, 1890 | }, 1891 | &NodeAttrSRCaps{ 1892 | MplsIPv4: true, 1893 | MplsIPv6: true, 1894 | RangeSIDLabel: []RangeSIDLabel{ 1895 | RangeSIDLabel{ 1896 | RangeSize: 1, 1897 | SIDLabel: &SIDLabelSID{ 1898 | SID: 2, 1899 | }, 1900 | }, 1901 | }, 1902 | }, 1903 | &NodeAttrSRAlgo{ 1904 | Algos: []uint8{1}, 1905 | }, 1906 | &NodeAttrSRLocalBlock{ 1907 | RangeSIDLabel: []RangeSIDLabel{ 1908 | RangeSIDLabel{ 1909 | RangeSize: 1, 1910 | SIDLabel: &SIDLabelLabel{ 1911 | Label: 2, 1912 | }, 1913 | }, 1914 | }, 1915 | }, 1916 | &NodeAttrSRMSPref{ 1917 | Preference: 2, 1918 | }, 1919 | }, 1920 | LinkAttrs: []LinkAttr{ 1921 | &LinkAttrRemoteIPv4RouterID{ 1922 | Address: net.ParseIP("172.16.1.202").To4(), 1923 | }, 1924 | &LinkAttrRemoteIPv6RouterID{ 1925 | Address: net.ParseIP("2601::2"), 1926 | }, 1927 | &LinkAttrAdminGroup{ 1928 | Group: adminGroup, 1929 | }, 1930 | &LinkAttrMaxLinkBandwidth{ 1931 | BytesPerSecond: 10000, 1932 | }, 1933 | &LinkAttrMaxReservableLinkBandwidth{ 1934 | BytesPerSecond: 20000, 1935 | }, 1936 | &LinkAttrUnreservedBandwidth{ 1937 | BytesPerSecond: unreservedBW, 1938 | }, 1939 | &LinkAttrTEDefaultMetric{ 1940 | Metric: uint32(50), 1941 | }, 1942 | &LinkAttrLinkProtectionType{ 1943 | ExtraTraffic: true, 1944 | Unprotected: true, 1945 | Shared: true, 1946 | DedicatedOneToOne: true, 1947 | DedicatedOnePlusOne: true, 1948 | Enhanced: true, 1949 | }, 1950 | &LinkAttrMplsProtocolMask{ 1951 | LDP: true, 1952 | RsvpTE: true, 1953 | }, 1954 | &LinkAttrIgpMetric{ 1955 | Type: LinkAttrIgpMetricIsIsSmallType, 1956 | Metric: 42, 1957 | }, 1958 | &LinkAttrIgpMetric{ 1959 | Type: LinkAttrIgpMetricOspfType, 1960 | Metric: 42, 1961 | }, 1962 | &LinkAttrIgpMetric{ 1963 | Type: LinkAttrIgpMetricIsIsWideType, 1964 | Metric: 42, 1965 | }, 1966 | &LinkAttrSharedRiskLinkGroup{ 1967 | Groups: []uint32{24, 15, 16}, 1968 | }, 1969 | &LinkAttrOpaqueLinkAttr{ 1970 | Data: []byte{1, 2, 3, 4}, 1971 | }, 1972 | &LinkAttrLinkName{ 1973 | Name: "test", 1974 | }, 1975 | &LinkAttrPeerNodeSID{ 1976 | Weight: 2, 1977 | SIDIndexLabel: &SIDIndexLabelOffset{ 1978 | Offset: 2, 1979 | }, 1980 | }, 1981 | &LinkAttrPeerAdjSID{ 1982 | Value: true, 1983 | Local: true, 1984 | Backup: true, 1985 | Persistent: true, 1986 | Weight: 2, 1987 | SIDIndexLabel: &SIDIndexLabelLabel{ 1988 | Label: 2, 1989 | }, 1990 | }, 1991 | &LinkAttrPeerSetSID{ 1992 | Weight: 2, 1993 | SIDIndexLabel: &SIDIndexLabelLabel{ 1994 | Label: 2, 1995 | }, 1996 | }, 1997 | &LinkAttrAdjSID{ 1998 | Flags: &LinkAttrAdjSIDFlagsOspf{ 1999 | Backup: true, 2000 | Value: true, 2001 | Local: true, 2002 | Group: true, 2003 | Persistent: true, 2004 | }, 2005 | Weight: 2, 2006 | SIDIndexLabel: &SIDIndexLabelLabel{ 2007 | Label: 2, 2008 | }, 2009 | }, 2010 | &LinkAttrLanAdjSID{ 2011 | Flags: &LinkAttrAdjSIDFlagsOspf{}, 2012 | Weight: 2, 2013 | NeighborIDSystemID: &LinkAttrLanAdjSIDProtoSpecificIDOspf{ 2014 | NeighborID: 2, 2015 | }, 2016 | SIDIndexLabel: &SIDIndexLabelLabel{ 2017 | Label: 2, 2018 | }, 2019 | }, 2020 | &LinkAttrUniLinkDelay{ 2021 | Anomalous: true, 2022 | Delay: time.Second * 1, 2023 | }, 2024 | &LinkAttrMinMaxUniLinkDelay{ 2025 | Anomalous: true, 2026 | MinDelay: time.Second * 1, 2027 | MaxDelay: time.Second * 1, 2028 | }, 2029 | &LinkAttrUniDelayVariation{ 2030 | DelayVariation: time.Second * 1, 2031 | }, 2032 | &LinkAttrUniPacketLoss{ 2033 | LossPercent: packetLossUnit * 3, 2034 | }, 2035 | &LinkAttrUniResidualBandwidth{ 2036 | BytesPerSecond: 1000, 2037 | }, 2038 | &LinkAttrUniAvailableBandwidth{ 2039 | BytesPerSecond: 1000, 2040 | }, 2041 | &LinkAttrUniBandwidthUtil{ 2042 | BytesPerSecond: 1000, 2043 | }, 2044 | &LinkAttrL2BundleMember{ 2045 | MemberDescriptor: 2, 2046 | LinkAttrs: []LinkAttr{ 2047 | &LinkAttrIgpMetric{ 2048 | Metric: 2, 2049 | }, 2050 | }, 2051 | }, 2052 | }, 2053 | PrefixAttrs: []PrefixAttr{ 2054 | &PrefixAttrIgpFlags{ 2055 | IsIsDown: true, 2056 | OspfNoUnicast: true, 2057 | OspfLocalAddress: true, 2058 | OspfPropagateNssa: true, 2059 | }, 2060 | &PrefixAttrIgpRouteTag{ 2061 | Tags: []uint32{1, 2, 3, 4}, 2062 | }, 2063 | &PrefixAttrIgpExtendedRouteTag{ 2064 | Tags: []uint64{1, 2, 3, 4}, 2065 | }, 2066 | &PrefixAttrPrefixMetric{ 2067 | Metric: 35, 2068 | }, 2069 | &PrefixAttrOspfForwardingAddress{ 2070 | Address: net.ParseIP("172.16.1.201").To4(), 2071 | }, 2072 | &PrefixAttrOspfForwardingAddress{ 2073 | Address: net.ParseIP("2601::1"), 2074 | }, 2075 | &PrefixAttrOpaquePrefixAttribute{ 2076 | Data: []byte{1, 2, 3, 4}, 2077 | }, 2078 | &PrefixAttrPrefixSID{ 2079 | Flags: &PrefixAttrPrefixSIDFlagsOspf{ 2080 | NoPHP: true, 2081 | MappingServer: true, 2082 | ExplicitNull: true, 2083 | Value: true, 2084 | Local: true, 2085 | }, 2086 | Algorithm: 2, 2087 | SIDIndexLabel: &SIDIndexLabelLabel{ 2088 | Label: 2, 2089 | }, 2090 | }, 2091 | &PrefixAttrRange{ 2092 | Flags: &PrefixAttrRangeFlagsOspf{ 2093 | InterArea: true, 2094 | }, 2095 | RangeSize: 2, 2096 | PrefixSID: []*PrefixAttrPrefixSID{ 2097 | &PrefixAttrPrefixSID{ 2098 | Flags: &PrefixAttrPrefixSIDFlagsOspf{ 2099 | NoPHP: true, 2100 | MappingServer: true, 2101 | ExplicitNull: true, 2102 | Value: true, 2103 | Local: true, 2104 | }, 2105 | Algorithm: 2, 2106 | SIDIndexLabel: &SIDIndexLabelLabel{ 2107 | Label: 2, 2108 | }, 2109 | }, 2110 | }, 2111 | }, 2112 | &PrefixAttrFlagsOSPFv2{ 2113 | Attach: true, 2114 | Node: true, 2115 | }, 2116 | &PrefixAttrSourceRouterID{ 2117 | RouterID: net.ParseIP("172.16.1.1").To4(), 2118 | }, 2119 | }, 2120 | }, 2121 | } 2122 | 2123 | u := &UpdateMessage{ 2124 | PathAttrs: attrs, 2125 | } 2126 | 2127 | assert.Equal(t, u.MessageType(), UpdateMessageType) 2128 | 2129 | b, err := u.serialize() 2130 | if err != nil { 2131 | t.Fatal(err) 2132 | } 2133 | 2134 | m, err := messagesFromBytes(b) 2135 | if err != nil { 2136 | t.Fatal(err) 2137 | } 2138 | 2139 | if len(m) != 1 { 2140 | t.Fatal("invalid length of messages deserialized") 2141 | } 2142 | 2143 | um, ok := m[0].(*UpdateMessage) 2144 | if !ok { 2145 | t.Fatal("not an update message") 2146 | } 2147 | 2148 | if !assert.Equal(t, len(um.PathAttrs), len(attrs)) { 2149 | t.Fatal("attr len not equal") 2150 | } 2151 | 2152 | for i, a := range attrs { 2153 | assert.Equal(t, a, um.PathAttrs[i]) 2154 | } 2155 | } 2156 | --------------------------------------------------------------------------------