diff --git a/protocols/bgp/packet/decoder.go b/protocols/bgp/packet/decoder.go index e473f3edf38c208f5726ecc3b776dbf883f61197..cafc9b5a21165c5f0b909420335e2fcfe3c62e10 100644 --- a/protocols/bgp/packet/decoder.go +++ b/protocols/bgp/packet/decoder.go @@ -9,14 +9,18 @@ import ( "github.com/taktv6/tflow2/convert" ) +type DecodingOptions struct { + Supports4OctetASN bool +} + // Decode decodes a BGP message -func Decode(buf *bytes.Buffer) (*BGPMessage, error) { +func Decode(buf *bytes.Buffer, opt *DecodingOptions) (*BGPMessage, error) { hdr, err := decodeHeader(buf) if err != nil { return nil, fmt.Errorf("Failed to decode header: %v", err) } - body, err := decodeMsgBody(buf, hdr.Type, hdr.Length-MinLen) + body, err := decodeMsgBody(buf, hdr.Type, hdr.Length-MinLen, opt) if err != nil { return nil, fmt.Errorf("Failed to decode message: %v", err) } @@ -27,12 +31,12 @@ func Decode(buf *bytes.Buffer) (*BGPMessage, error) { }, nil } -func decodeMsgBody(buf *bytes.Buffer, msgType uint8, l uint16) (interface{}, error) { +func decodeMsgBody(buf *bytes.Buffer, msgType uint8, l uint16, opt *DecodingOptions) (interface{}, error) { switch msgType { case OpenMsg: return decodeOpenMsg(buf) case UpdateMsg: - return decodeUpdateMsg(buf, l) + return decodeUpdateMsg(buf, l, opt) case KeepaliveMsg: return nil, nil // Nothing to decode in Keepalive message case NotificationMsg: @@ -41,7 +45,7 @@ func decodeMsgBody(buf *bytes.Buffer, msgType uint8, l uint16) (interface{}, err return nil, fmt.Errorf("Unknown message type: %d", msgType) } -func decodeUpdateMsg(buf *bytes.Buffer, l uint16) (*BGPUpdate, error) { +func decodeUpdateMsg(buf *bytes.Buffer, l uint16, opt *DecodingOptions) (*BGPUpdate, error) { msg := &BGPUpdate{} err := decode(buf, []interface{}{&msg.WithdrawnRoutesLen}) @@ -59,7 +63,7 @@ func decodeUpdateMsg(buf *bytes.Buffer, l uint16) (*BGPUpdate, error) { return msg, err } - msg.PathAttributes, err = decodePathAttrs(buf, msg.TotalPathAttrLen) + msg.PathAttributes, err = decodePathAttrs(buf, msg.TotalPathAttrLen, opt) if err != nil { return msg, err } diff --git a/protocols/bgp/packet/decoder_test.go b/protocols/bgp/packet/decoder_test.go index 916660a3dd06e08b23d29ea4a8c48fb63ed68b47..a2ad9771d80f102ca8acccd2ff5d6a362160e566 100644 --- a/protocols/bgp/packet/decoder_test.go +++ b/protocols/bgp/packet/decoder_test.go @@ -3,6 +3,7 @@ package packet import ( "bytes" "fmt" + "strconv" "testing" "github.com/bio-routing/bio-rd/net" @@ -70,7 +71,7 @@ func BenchmarkDecodeUpdateMsg(b *testing.B) { for i := 0; i < b.N; i++ { buf := bytes.NewBuffer(input) - _, err := decodeUpdateMsg(buf, uint16(len(input))) + _, err := decodeUpdateMsg(buf, uint16(len(input)), &DecodingOptions{}) if err != nil { fmt.Printf("decodeUpdateMsg failed: %v\n", err) } @@ -251,7 +252,7 @@ func TestDecode(t *testing.T) { for _, test := range tests { buf := bytes.NewBuffer(test.input) - msg, err := Decode(buf) + msg, err := Decode(buf, &DecodingOptions{}) if err != nil && !test.wantFail { t.Errorf("Unexpected error in test %d: %v", test.testNum, err) @@ -1369,9 +1370,9 @@ func TestDecodeUpdateMsg(t *testing.T) { }, { // 2 withdraws with four path attributes (Communities + AS4Path +AS4Aggregator + Origin), valid update - testNum: 19, + testNum: 20, input: []byte{0, 5, 8, 10, 16, 192, 168, - 0, 30, // Total Path Attribute Length + 0, 32, // Total Path Attribute Length 0, // Attribute flags 8, // Attribute Type code (Community) @@ -1379,10 +1380,12 @@ func TestDecodeUpdateMsg(t *testing.T) { 0, 0, 1, 0, // Arbitrary Community 0, 0, 1, 1, // Arbitrary Community - 128, // Attribute flags - 17, // Attribute Type code (AS4Path) - 4, // Length - 0, 0, 2, 3, // Arbitrary Bytes + 128, // Attribute flags + 17, // Attribute Type code (AS4Path) + 6, // Length + 2, // AS_SEQUENCE + 1, // Number of ASNs + 0x00, 0x03, 0x17, 0xf3, // 202739 128, // Attribute flags 18, // Attribute Type code (AS4Aggregator) @@ -1406,7 +1409,7 @@ func TestDecodeUpdateMsg(t *testing.T) { Pfxlen: 16, }, }, - TotalPathAttrLen: 30, + TotalPathAttrLen: 32, PathAttributes: &PathAttribute{ Optional: false, Transitive: false, @@ -1420,9 +1423,15 @@ func TestDecodeUpdateMsg(t *testing.T) { Transitive: false, Partial: false, ExtendedLength: false, - Length: 4, + Length: 6, TypeCode: 17, - Value: uint32(515), + Value: ASPath{ + ASPathSegment{ + Type: 2, + Count: 1, + ASNs: []uint32{202739}, + }, + }, Next: &PathAttribute{ Optional: true, Transitive: false, @@ -1447,29 +1456,31 @@ func TestDecodeUpdateMsg(t *testing.T) { }, } + t.Parallel() + for _, test := range tests { - buf := bytes.NewBuffer(test.input) - l := test.explicitLength - if l == 0 { - l = uint16(len(test.input)) - } - msg, err := decodeUpdateMsg(buf, l) + t.Run(strconv.Itoa(test.testNum), func(t *testing.T) { + buf := bytes.NewBuffer(test.input) + l := test.explicitLength + if l == 0 { + l = uint16(len(test.input)) + } + msg, err := decodeUpdateMsg(buf, l, &DecodingOptions{}) - if err != nil && !test.wantFail { - t.Errorf("Unexpected error in test %d: %v", test.testNum, err) - continue - } + if err != nil && !test.wantFail { + t.Fatalf("Unexpected error in test %d: %v", test.testNum, err) + } - if err == nil && test.wantFail { - t.Errorf("Expected error did not happen in test %d", test.testNum) - continue - } + if err == nil && test.wantFail { + t.Fatalf("Expected error did not happen in test %d", test.testNum) + } - if err != nil && test.wantFail { - continue - } + if err != nil && test.wantFail { + return + } - assert.Equalf(t, test.expected, msg, "%d", test.testNum) + assert.Equalf(t, test.expected, msg, "%d", test.testNum) + }) } } @@ -1490,7 +1501,7 @@ func TestDecodeMsgBody(t *testing.T) { } for _, test := range tests { - res, err := decodeMsgBody(test.buffer, test.msgType, test.length) + res, err := decodeMsgBody(test.buffer, test.msgType, test.length, &DecodingOptions{}) if test.wantFail && err == nil { t.Errorf("Expected error dit not happen in test %q", test.name) } diff --git a/protocols/bgp/packet/encoder_test.go b/protocols/bgp/packet/encoder_test.go index 0d4f70a3483dabd8f7332dd148c72b09df626dee..fd7405d735a5d11a4117d144e7086828c86f5628 100644 --- a/protocols/bgp/packet/encoder_test.go +++ b/protocols/bgp/packet/encoder_test.go @@ -70,7 +70,7 @@ func TestSerializeOpenMsg(t *testing.T) { name: "Valid #1", input: &BGPOpen{ Version: 4, - AS: 15169, + ASN: 15169, HoldTime: 120, BGPIdentifier: convert.Uint32([]byte{100, 111, 120, 130}), OptParmLen: 0, diff --git a/protocols/bgp/packet/path_attributes.go b/protocols/bgp/packet/path_attributes.go index 3b3192034590bd357b29a13833a2bcaaa632f0cf..d040718eaaf6aae2286938f6a3d97c5a039a7daf 100644 --- a/protocols/bgp/packet/path_attributes.go +++ b/protocols/bgp/packet/path_attributes.go @@ -9,7 +9,7 @@ import ( "github.com/taktv6/tflow2/convert" ) -func decodePathAttrs(buf *bytes.Buffer, tpal uint16) (*PathAttribute, error) { +func decodePathAttrs(buf *bytes.Buffer, tpal uint16, opt *DecodingOptions) (*PathAttribute, error) { var ret *PathAttribute var eol *PathAttribute var pa *PathAttribute @@ -18,7 +18,7 @@ func decodePathAttrs(buf *bytes.Buffer, tpal uint16) (*PathAttribute, error) { p := uint16(0) for p < tpal { - pa, consumed, err = decodePathAttr(buf) + pa, consumed, err = decodePathAttr(buf, opt) if err != nil { return nil, fmt.Errorf("Unable to decode path attr: %v", err) } @@ -36,7 +36,7 @@ func decodePathAttrs(buf *bytes.Buffer, tpal uint16) (*PathAttribute, error) { return ret, nil } -func decodePathAttr(buf *bytes.Buffer) (pa *PathAttribute, consumed uint16, err error) { +func decodePathAttr(buf *bytes.Buffer, opt *DecodingOptions) (pa *PathAttribute, consumed uint16, err error) { pa = &PathAttribute{} err = decodePathAttrFlags(buf, pa) @@ -63,9 +63,18 @@ func decodePathAttr(buf *bytes.Buffer) (pa *PathAttribute, consumed uint16, err return nil, consumed, fmt.Errorf("Failed to decode Origin: %v", err) } case ASPathAttr: - if err := pa.decodeASPath(buf); err != nil { + asnLength := uint8(2) + if opt.Supports4OctetASN { + asnLength = 4 + } + + if err := pa.decodeASPath(buf, asnLength); err != nil { return nil, consumed, fmt.Errorf("Failed to decode AS Path: %v", err) } + case AS4PathAttr: + if err := pa.decodeASPath(buf, 4); err != nil { + return nil, consumed, fmt.Errorf("Failed to decode AS4 Path: %v", err) + } case NextHopAttr: if err := pa.decodeNextHop(buf); err != nil { return nil, consumed, fmt.Errorf("Failed to decode Next-Hop: %v", err) @@ -88,10 +97,6 @@ func decodePathAttr(buf *bytes.Buffer) (pa *PathAttribute, consumed uint16, err if err := pa.decodeCommunities(buf); err != nil { return nil, consumed, fmt.Errorf("Failed to decode Community: %v", err) } - case AS4PathAttr: - if err := pa.decodeAS4Path(buf); err != nil { - return nil, consumed, fmt.Errorf("Failed to skip not supported AS4Path: %v", err) - } case AS4AggregatorAttr: if err := pa.decodeAS4Aggregator(buf); err != nil { return nil, consumed, fmt.Errorf("Failed to skip not supported AS4Aggregator: %v", err) @@ -139,14 +144,11 @@ func (pa *PathAttribute) decodeOrigin(buf *bytes.Buffer) error { return dumpNBytes(buf, pa.Length-p) } -func (pa *PathAttribute) decodeASPath(buf *bytes.Buffer) error { +func (pa *PathAttribute) decodeASPath(buf *bytes.Buffer, asnLength uint8) error { pa.Value = make(ASPath, 0) - p := uint16(0) for p < pa.Length { - segment := ASPathSegment{ - ASNs: make([]uint32, 0), - } + segment := ASPathSegment{} err := decode(buf, []interface{}{&segment.Type, &segment.Count}) if err != nil { @@ -162,23 +164,51 @@ func (pa *PathAttribute) decodeASPath(buf *bytes.Buffer) error { return fmt.Errorf("Invalid AS Path segment length: %d", segment.Count) } + segment.ASNs = make([]uint32, segment.Count) for i := uint8(0); i < segment.Count; i++ { - asn := uint16(0) - - err := decode(buf, []interface{}{&asn}) + asn, err := pa.decodeASN(buf, asnLength) if err != nil { return err } - p += 2 + p += uint16(asnLength) - segment.ASNs = append(segment.ASNs, uint32(asn)) + segment.ASNs[i] = asn } + pa.Value = append(pa.Value.(ASPath), segment) } return nil } +func (pa *PathAttribute) decodeASN(buf *bytes.Buffer, asnSize uint8) (asn uint32, err error) { + if asnSize == 4 { + return pa.decode4ByteASN(buf) + } + + return pa.decode2ByteASN(buf) +} + +func (pa *PathAttribute) decode4ByteASN(buf *bytes.Buffer) (asn uint32, err error) { + asn4 := uint32(0) + err = decode(buf, []interface{}{&asn4}) + if err != nil { + return 0, err + } + + return uint32(asn4), nil +} + +func (pa *PathAttribute) decode2ByteASN(buf *bytes.Buffer) (asn uint32, err error) { + asn4 := uint16(0) + err = decode(buf, []interface{}{&asn4}) + if err != nil { + return 0, err + } + + return uint32(asn4), nil +} + func (pa *PathAttribute) decodeNextHop(buf *bytes.Buffer) error { return pa.decodeUint32(buf, "next hop") } @@ -273,10 +303,6 @@ func (pa *PathAttribute) decodeLargeCommunities(buf *bytes.Buffer) error { return nil } -func (pa *PathAttribute) decodeAS4Path(buf *bytes.Buffer) error { - return pa.decodeUint32(buf, "AS4Path") -} - func (pa *PathAttribute) decodeAS4Aggregator(buf *bytes.Buffer) error { return pa.decodeUint32(buf, "AS4Aggregator") } diff --git a/protocols/bgp/packet/path_attributes_test.go b/protocols/bgp/packet/path_attributes_test.go index 248b731714389096a62e2381a548780beb8318e7..5b816cc49aefe865f9afcebdfbe0e359793e3cbe 100644 --- a/protocols/bgp/packet/path_attributes_test.go +++ b/protocols/bgp/packet/path_attributes_test.go @@ -50,7 +50,7 @@ func TestDecodePathAttrs(t *testing.T) { } for _, test := range tests { - res, err := decodePathAttrs(bytes.NewBuffer(test.input), uint16(len(test.input))) + res, err := decodePathAttrs(bytes.NewBuffer(test.input), uint16(len(test.input)), &DecodingOptions{}) if test.wantFail && err == nil { t.Errorf("Expected error did not happen for test %q", test.name) @@ -173,7 +173,7 @@ func TestDecodePathAttr(t *testing.T) { } for _, test := range tests { - res, _, err := decodePathAttr(bytes.NewBuffer(test.input)) + res, _, err := decodePathAttr(bytes.NewBuffer(test.input), &DecodingOptions{}) if test.wantFail && err == nil { t.Errorf("Expected error did not happen for test %q", test.name) @@ -264,6 +264,7 @@ func TestDecodeASPath(t *testing.T) { input []byte wantFail bool explicitLength uint16 + use4OctetASNs bool expected *PathAttribute }{ { @@ -308,6 +309,28 @@ func TestDecodeASPath(t *testing.T) { }, }, }, + { + name: "32 bit ASNs in AS_PATH", + input: []byte{ + 1, // AS_SEQUENCE + 3, // Path Length + 0, 0, 0, 100, 0, 0, 0, 222, 0, 0, 0, 240, + }, + wantFail: false, + use4OctetASNs: true, + expected: &PathAttribute{ + Length: 14, + Value: ASPath{ + ASPathSegment{ + Type: 1, + Count: 3, + ASNs: []uint32{ + 100, 222, 240, + }, + }, + }, + }, + }, { name: "Empty input", input: []byte{}, @@ -326,28 +349,36 @@ func TestDecodeASPath(t *testing.T) { } for _, test := range tests { - l := uint16(len(test.input)) - if test.explicitLength != 0 { - l = test.explicitLength - } - pa := &PathAttribute{ - Length: l, - } - err := pa.decodeASPath(bytes.NewBuffer(test.input)) + t.Run(test.name, func(t *testing.T) { + l := uint16(len(test.input)) + if test.explicitLength != 0 { + l = test.explicitLength + } + pa := &PathAttribute{ + Length: l, + } - if test.wantFail && err == nil { - t.Errorf("Expected error did not happen for test %q", test.name) - } + asnLength := uint8(2) + if test.use4OctetASNs { + asnLength = 4 + } - if !test.wantFail && err != nil { - t.Errorf("Unexpected failure for test %q: %v", test.name, err) - } + err := pa.decodeASPath(bytes.NewBuffer(test.input), asnLength) - if err != nil { - continue - } + if test.wantFail && err == nil { + t.Errorf("Expected error did not happen for test %q", test.name) + } - assert.Equal(t, test.expected, pa) + if !test.wantFail && err != nil { + t.Errorf("Unexpected failure for test %q: %v", test.name, err) + } + + if err != nil { + return + } + + assert.Equal(t, test.expected, pa) + }) } } diff --git a/protocols/bgp/server/fsm.go b/protocols/bgp/server/fsm.go index c4754257af98f0076e60a402b01f06966d4a0218..93223c7e5318ee38d2d1f9d77001bf1a34944bc1 100644 --- a/protocols/bgp/server/fsm.go +++ b/protocols/bgp/server/fsm.go @@ -56,6 +56,8 @@ type FSM struct { capAddPathSend bool capAddPathRecv bool + decodingOptions *packet.DecodingOptions + local net.IP ribsInitialized bool @@ -99,6 +101,7 @@ func newFSM2(peer *Peer) *FSM { msgRecvFailCh: make(chan error), stopMsgRecvCh: make(chan struct{}), rib: peer.rib, + decodingOptions: &packet.DecodingOptions{}, } } diff --git a/protocols/bgp/server/fsm_established.go b/protocols/bgp/server/fsm_established.go index ca68aecc2fe61957bc10d3582e6c7855a2ef0b0f..63d56e528c1ec7b230d2f8351ad6d3a6805bb22f 100644 --- a/protocols/bgp/server/fsm_established.go +++ b/protocols/bgp/server/fsm_established.go @@ -143,7 +143,7 @@ func (s *establishedState) keepaliveTimerExpired() (state, string) { } func (s *establishedState) msgReceived(data []byte) (state, string) { - msg, err := packet.Decode(bytes.NewBuffer(data)) + msg, err := packet.Decode(bytes.NewBuffer(data), s.fsm.decodingOptions) if err != nil { switch bgperr := err.(type) { case packet.BGPError: diff --git a/protocols/bgp/server/fsm_open_confirm.go b/protocols/bgp/server/fsm_open_confirm.go index d2d25e4f533ec30d9a5d15aec25504d055c45f54..75947171112f5c29b528a25cd479a87cea594fe8 100644 --- a/protocols/bgp/server/fsm_open_confirm.go +++ b/protocols/bgp/server/fsm_open_confirm.go @@ -82,7 +82,7 @@ func (s *openConfirmState) keepaliveTimerExpired() (state, string) { } func (s *openConfirmState) msgReceived(data []byte) (state, string) { - msg, err := packet.Decode(bytes.NewBuffer(data)) + msg, err := packet.Decode(bytes.NewBuffer(data), s.fsm.decodingOptions) if err != nil { switch bgperr := err.(type) { case packet.BGPError: diff --git a/protocols/bgp/server/fsm_open_sent.go b/protocols/bgp/server/fsm_open_sent.go index aba1193dd9b75010e50a3d4e2de932e9bc87647e..afbad880c40381989075e30947b63d1d03031188 100644 --- a/protocols/bgp/server/fsm_open_sent.go +++ b/protocols/bgp/server/fsm_open_sent.go @@ -74,7 +74,7 @@ func (s *openSentState) holdTimerExpired() (state, string) { } func (s *openSentState) msgReceived(data []byte) (state, string) { - msg, err := packet.Decode(bytes.NewBuffer(data)) + msg, err := packet.Decode(bytes.NewBuffer(data), s.fsm.decodingOptions) if err != nil { switch bgperr := err.(type) { case packet.BGPError: @@ -199,6 +199,8 @@ func (s *openSentState) processAddPathCapability(addPathCap packet.AddPathCapabi } func (s *openSentState) processASN4Capability(cap packet.ASN4Capability) { + s.fsm.decodingOptions.Supports4OctetASN = true + if s.peerASNRcvd == packet.ASTransASN { s.peerASNRcvd = cap.ASN4 }