diff --git a/main.go b/main.go index 7220ba4a6c669dd44368ccbc81c16e72c534d0d7..7c5bd7f1ba15450a5679688079307d3e0428e626 100644 --- a/main.go +++ b/main.go @@ -40,9 +40,9 @@ func main() { b.AddPeer(config.Peer{ AdminEnabled: true, - LocalAS: 6695, + LocalAS: 65200, PeerAS: 65300, - PeerAddress: net.IP([]byte{169, 254, 200, 1}), + PeerAddress: net.IP([]byte{172, 17, 0, 3}), LocalAddress: net.IP([]byte{169, 254, 200, 0}), ReconnectInterval: time.Second * 15, HoldTime: time.Second * 90, @@ -59,9 +59,9 @@ func main() { b.AddPeer(config.Peer{ AdminEnabled: true, - LocalAS: 6695, + LocalAS: 65200, PeerAS: 65100, - PeerAddress: net.IP([]byte{169, 254, 100, 0}), + PeerAddress: net.IP([]byte{172, 17, 0, 2}), LocalAddress: net.IP([]byte{169, 254, 100, 1}), ReconnectInterval: time.Second * 15, HoldTime: time.Second * 90, diff --git a/protocols/bgp/packet/BUILD.bazel b/protocols/bgp/packet/BUILD.bazel index 10c8af2338d9c6ce76bb2f37d23355b04d21af69..66762a51dfcce62cd24b01dbc3d288dd7c26fbcf 100644 --- a/protocols/bgp/packet/BUILD.bazel +++ b/protocols/bgp/packet/BUILD.bazel @@ -9,6 +9,7 @@ go_library( "encoder.go", "large_community.go", "nlri.go", + "options.go", "parameters.go", "path_attribute_flags.go", "path_attributes.go", diff --git a/protocols/bgp/packet/bgp.go b/protocols/bgp/packet/bgp.go index 622b46f37b115dd27779a285284d177d75695230..8082f5dd8c02ae2c74a23013bcdb4c63d524d52b 100644 --- a/protocols/bgp/packet/bgp.go +++ b/protocols/bgp/packet/bgp.go @@ -92,9 +92,11 @@ const ( UnicastSAFI = 1 CapabilitiesParamType = 2 AddPathCapabilityCode = 69 + ASN4CapabilityCode = 65 AddPathReceive = 1 AddPathSend = 2 AddPathSendReceive = 3 + ASTransASN = 23456 ) type BGPError struct { @@ -119,7 +121,7 @@ type BGPHeader struct { type BGPOpen struct { Version uint8 - AS uint16 + ASN uint16 HoldTime uint16 BGPIdentifier uint32 OptParmLen uint8 diff --git a/protocols/bgp/packet/decoder.go b/protocols/bgp/packet/decoder.go index 31583bf1b68257625dfaa3d8b8e40893982d6713..c4d583f2138edfbbfb5b9be33b2a1c11764ee0bd 100644 --- a/protocols/bgp/packet/decoder.go +++ b/protocols/bgp/packet/decoder.go @@ -10,13 +10,13 @@ import ( ) // Decode decodes a BGP message -func Decode(buf *bytes.Buffer) (*BGPMessage, error) { +func Decode(buf *bytes.Buffer, opt *Options) (*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 +27,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 *Options) (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 +41,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 *Options) (*BGPUpdate, error) { msg := &BGPUpdate{} err := decode(buf, []interface{}{&msg.WithdrawnRoutesLen}) @@ -59,7 +59,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 } @@ -141,7 +141,7 @@ func _decodeOpenMsg(buf *bytes.Buffer) (interface{}, error) { fields := []interface{}{ &msg.Version, - &msg.AS, + &msg.ASN, &msg.HoldTime, &msg.BGPIdentifier, &msg.OptParmLen, @@ -238,6 +238,12 @@ func decodeCapability(buf *bytes.Buffer) (Capability, error) { return cap, fmt.Errorf("Unable to decode add path capability") } cap.Value = addPathCap + case ASN4CapabilityCode: + asn4Cap, err := decodeASN4Capability(buf) + if err != nil { + return cap, fmt.Errorf("Unable to decode 4 octet ASN capability") + } + cap.Value = asn4Cap default: for i := uint8(0); i < cap.Length; i++ { _, err := buf.ReadByte() @@ -266,6 +272,20 @@ func decodeAddPathCapability(buf *bytes.Buffer) (AddPathCapability, error) { return addPathCap, nil } +func decodeASN4Capability(buf *bytes.Buffer) (ASN4Capability, error) { + asn4Cap := ASN4Capability{} + fields := []interface{}{ + &asn4Cap.ASN4, + } + + err := decode(buf, fields) + if err != nil { + return asn4Cap, err + } + + return asn4Cap, nil +} + func validateOpen(msg *BGPOpen) error { if msg.Version != BGP4Version { return BGPError{ diff --git a/protocols/bgp/packet/decoder_test.go b/protocols/bgp/packet/decoder_test.go index a9ae237b9cb94a3e0ec6af0b988bc09f41d3f806..56416186ba1d714a13fb4f248281d3d5f48451dd 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)), &Options{}) if err != nil { fmt.Printf("decodeUpdateMsg failed: %v\n", err) } @@ -156,7 +157,7 @@ func TestDecode(t *testing.T) { }, Body: &BGPOpen{ Version: 4, - AS: 200, + ASN: 200, HoldTime: 15, BGPIdentifier: uint32(169090600), OptParmLen: 0, @@ -184,7 +185,7 @@ func TestDecode(t *testing.T) { }, Body: &BGPOpen{ Version: 4, - AS: 200, + ASN: 200, HoldTime: 15, BGPIdentifier: uint32(100), }, @@ -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, &Options{}) 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, &Options{}) - 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, &Options{}) if test.wantFail && err == nil { t.Errorf("Expected error dit not happen in test %q", test.name) } @@ -1512,7 +1523,7 @@ func TestDecodeOpenMsg(t *testing.T) { wantFail: false, expected: &BGPOpen{ Version: 4, - AS: 257, + ASN: 257, HoldTime: 15, BGPIdentifier: 169090600, OptParmLen: 0, diff --git a/protocols/bgp/packet/encoder.go b/protocols/bgp/packet/encoder.go index ea87d1ff64cfc946688e738ae9f7ffa372cf10c7..ac9bc4483199b81f4e8ecc25c6b91e3ec8361b9c 100644 --- a/protocols/bgp/packet/encoder.go +++ b/protocols/bgp/packet/encoder.go @@ -35,7 +35,7 @@ func SerializeOpenMsg(msg *BGPOpen) []byte { serializeHeader(buf, openLen, OpenMsg) buf.WriteByte(msg.Version) - buf.Write(convert.Uint16Byte(msg.AS)) + buf.Write(convert.Uint16Byte(msg.ASN)) buf.Write(convert.Uint16Byte(msg.HoldTime)) buf.Write(convert.Uint32Byte(msg.BGPIdentifier)) @@ -63,7 +63,7 @@ func serializeHeader(buf *bytes.Buffer, length uint16, typ uint8) { buf.WriteByte(typ) } -func (b *BGPUpdateAddPath) SerializeUpdate() ([]byte, error) { +func (b *BGPUpdateAddPath) SerializeUpdate(opt *Options) ([]byte, error) { budget := MaxLen - MinLen buf := bytes.NewBuffer(nil) @@ -78,7 +78,7 @@ func (b *BGPUpdateAddPath) SerializeUpdate() ([]byte, error) { pathAttributesBuf := bytes.NewBuffer(nil) for pa := b.PathAttributes; pa != nil; pa = pa.Next { - paLen := int(pa.serialize(pathAttributesBuf)) + paLen := int(pa.serialize(pathAttributesBuf, opt)) budget -= paLen if budget < 0 { return nil, fmt.Errorf("update too long") @@ -122,7 +122,7 @@ func (b *BGPUpdateAddPath) SerializeUpdate() ([]byte, error) { return buf.Bytes(), nil } -func (b *BGPUpdate) SerializeUpdate() ([]byte, error) { +func (b *BGPUpdate) SerializeUpdate(opt *Options) ([]byte, error) { budget := MaxLen - MinLen buf := bytes.NewBuffer(nil) @@ -137,7 +137,7 @@ func (b *BGPUpdate) SerializeUpdate() ([]byte, error) { pathAttributesBuf := bytes.NewBuffer(nil) for pa := b.PathAttributes; pa != nil; pa = pa.Next { - paLen := int(pa.serialize(pathAttributesBuf)) + paLen := int(pa.serialize(pathAttributesBuf, opt)) budget -= paLen if budget < 0 { return nil, fmt.Errorf("update too long") 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/options.go b/protocols/bgp/packet/options.go new file mode 100644 index 0000000000000000000000000000000000000000..b2d35bc60cbe17490f94314c28da26de9a19e40a --- /dev/null +++ b/protocols/bgp/packet/options.go @@ -0,0 +1,5 @@ +package packet + +type Options struct { + Supports4OctetASN bool +} diff --git a/protocols/bgp/packet/parameters.go b/protocols/bgp/packet/parameters.go index 8edf42fc58e05bb3b73cacd4272012844bedcd45..0ed9af66fe01f96600c02eeb2c00b69b11edc76c 100644 --- a/protocols/bgp/packet/parameters.go +++ b/protocols/bgp/packet/parameters.go @@ -52,3 +52,11 @@ func (a AddPathCapability) serialize(buf *bytes.Buffer) { buf.WriteByte(a.SAFI) buf.WriteByte(a.SendReceive) } + +type ASN4Capability struct { + ASN4 uint32 +} + +func (a ASN4Capability) serialize(buf *bytes.Buffer) { + buf.Write(convert.Uint32Byte(a.ASN4)) +} diff --git a/protocols/bgp/packet/path_attributes.go b/protocols/bgp/packet/path_attributes.go index 3b3192034590bd357b29a13833a2bcaaa632f0cf..acd5c78ab813c5b3cc6442f61067c85471366545 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 *Options) (*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 *Options) (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") } @@ -383,14 +409,14 @@ func dumpNBytes(buf *bytes.Buffer, n uint16) error { return nil } -func (pa *PathAttribute) serialize(buf *bytes.Buffer) uint8 { +func (pa *PathAttribute) serialize(buf *bytes.Buffer, opt *Options) uint8 { pathAttrLen := uint8(0) switch pa.TypeCode { case OriginAttr: pathAttrLen = pa.serializeOrigin(buf) case ASPathAttr: - pathAttrLen = pa.serializeASPath(buf) + pathAttrLen = pa.serializeASPath(buf, opt) case NextHopAttr: pathAttrLen = pa.serializeNextHop(buf) case MEDAttr: @@ -421,21 +447,32 @@ func (pa *PathAttribute) serializeOrigin(buf *bytes.Buffer) uint8 { return 4 } -func (pa *PathAttribute) serializeASPath(buf *bytes.Buffer) uint8 { +func (pa *PathAttribute) serializeASPath(buf *bytes.Buffer, opt *Options) uint8 { attrFlags := uint8(0) attrFlags = setTransitive(attrFlags) buf.WriteByte(attrFlags) buf.WriteByte(ASPathAttr) + asnLength := uint8(2) + if opt.Supports4OctetASN { + asnLength = 4 + } + length := uint8(0) segmentsBuf := bytes.NewBuffer(nil) for _, segment := range pa.Value.(ASPath) { segmentsBuf.WriteByte(segment.Type) segmentsBuf.WriteByte(uint8(len(segment.ASNs))) + for _, asn := range segment.ASNs { - segmentsBuf.Write(convert.Uint16Byte(uint16(asn))) + if asnLength == 2 { + segmentsBuf.Write(convert.Uint16Byte(uint16(asn))) + } else { + segmentsBuf.Write(convert.Uint32Byte(asn)) + } } - length += 2 + uint8(len(segment.ASNs))*2 + fmt.Println(segment.ASNs) + length += 2 + uint8(len(segment.ASNs))*asnLength } buf.WriteByte(length) diff --git a/protocols/bgp/packet/path_attributes_test.go b/protocols/bgp/packet/path_attributes_test.go index 248b731714389096a62e2381a548780beb8318e7..c3d5ecda2e4d484ba48809651248e4ecd1c82188 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)), &Options{}) 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), &Options{}) 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) + }) } } @@ -1239,6 +1270,7 @@ func TestSerializeASPath(t *testing.T) { input *PathAttribute expected []byte expectedLen uint8 + use32BitASN bool }{ { name: "Test #1", @@ -1265,17 +1297,49 @@ func TestSerializeASPath(t *testing.T) { }, expectedLen: 10, }, + { + name: "32bit ASN", + input: &PathAttribute{ + TypeCode: ASPathAttr, + Value: ASPath{ + { + Type: 2, // Sequence + ASNs: []uint32{ + 100, 200, 210, + }, + }, + }, + }, + expected: []byte{ + 64, // Attribute flags + 2, // Type + 14, // Length + 2, // AS_SEQUENCE + 3, // ASN count + 0, 0, 0, 100, // ASN 100 + 0, 0, 0, 200, // ASN 200 + 0, 0, 0, 210, // ASN 210 + }, + expectedLen: 16, + use32BitASN: true, + }, } + t.Parallel() + for _, test := range tests { - buf := bytes.NewBuffer(nil) - n := test.input.serializeASPath(buf) - if n != test.expectedLen { - t.Errorf("Unexpected length for test %q: %d", test.name, n) - continue - } + t.Run(test.name, func(t *testing.T) { + buf := bytes.NewBuffer(nil) + opt := &Options{ + Supports4OctetASN: test.use32BitASN, + } + n := test.input.serializeASPath(buf, opt) + if n != test.expectedLen { + t.Fatalf("Unexpected length for test %q: %d", test.name, n) + } - assert.Equal(t, test.expected, buf.Bytes()) + assert.Equal(t, test.expected, buf.Bytes()) + }) } } @@ -1561,7 +1625,8 @@ func TestSerialize(t *testing.T) { } for _, test := range tests { - res, err := test.msg.SerializeUpdate() + opt := &Options{} + res, err := test.msg.SerializeUpdate(opt) if err != nil { if test.wantFail { continue @@ -1767,7 +1832,8 @@ func TestSerializeAddPath(t *testing.T) { } for _, test := range tests { - res, err := test.msg.SerializeUpdate() + opt := &Options{} + res, err := test.msg.SerializeUpdate(opt) if err != nil { if test.wantFail { continue diff --git a/protocols/bgp/server/BUILD.bazel b/protocols/bgp/server/BUILD.bazel index a82fe9fb50cdd5b85b8e219da7b264c4cba06ef4..345c18806a64e01267a63dc585e74864867db36a 100644 --- a/protocols/bgp/server/BUILD.bazel +++ b/protocols/bgp/server/BUILD.bazel @@ -41,6 +41,8 @@ go_library( go_test( name = "go_default_test", srcs = [ + "fsm_open_sent_test.go", + "fsm_test.go", "server_test.go", "update_helper_test.go", "withdraw_test.go", diff --git a/protocols/bgp/server/fsm.go b/protocols/bgp/server/fsm.go index 0c5221391c80e1dbd7bf1262d2506192eba80924..cf970b40483511c2ecafc1d36597f66117de06ef 100644 --- a/protocols/bgp/server/fsm.go +++ b/protocols/bgp/server/fsm.go @@ -57,6 +57,8 @@ type FSM struct { capAddPathSend bool capAddPathRecv bool + options *packet.Options + local net.IP ribsInitialized bool @@ -100,6 +102,7 @@ func newFSM2(peer *peer) *FSM { msgRecvFailCh: make(chan error), stopMsgRecvCh: make(chan struct{}), rib: peer.rib, + options: &packet.Options{}, } } @@ -220,13 +223,7 @@ func (fsm *FSM) resetConnectRetryCounter() { } func (fsm *FSM) sendOpen() error { - msg := packet.SerializeOpenMsg(&packet.BGPOpen{ - Version: BGPVersion, - AS: uint16(fsm.peer.localASN), - HoldTime: uint16(fsm.peer.holdTime / time.Second), - BGPIdentifier: fsm.peer.server.routerID, - OptParams: fsm.peer.optOpenParams, - }) + msg := packet.SerializeOpenMsg(fsm.openMessage()) _, err := fsm.con.Write(msg) if err != nil { @@ -236,6 +233,24 @@ func (fsm *FSM) sendOpen() error { return nil } +func (fsm *FSM) openMessage() *packet.BGPOpen { + return &packet.BGPOpen{ + Version: BGPVersion, + ASN: fsm.local16BitASN(), + HoldTime: uint16(fsm.peer.holdTime / time.Second), + BGPIdentifier: fsm.peer.routerID, + OptParams: fsm.peer.optOpenParams, + } +} + +func (fsm *FSM) local16BitASN() uint16 { + if fsm.peer.localASN > uint32(^uint16(0)) { + return packet.ASTransASN + } + + return uint16(fsm.peer.localASN) +} + func (fsm *FSM) sendNotification(errorCode uint8, errorSubCode uint8) error { msg := packet.SerializeNotificationMsg(&packet.BGPNotification{}) diff --git a/protocols/bgp/server/fsm_established.go b/protocols/bgp/server/fsm_established.go index 58facab0cd4ad487245cfe431a73478d3ca455a8..66480407dc7e39e174b4f391017bfdc8c9f2e05f 100644 --- a/protocols/bgp/server/fsm_established.go +++ b/protocols/bgp/server/fsm_established.go @@ -158,7 +158,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.options) 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..07b8a6152dd64c56055c7cbd0745980e462c3886 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.options) 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 63680636d8e0c65afe1dad9ec9d1280c34e9c37d..848c76697fb3a415b7e56131b67d3576d0e01722 100644 --- a/protocols/bgp/server/fsm_open_sent.go +++ b/protocols/bgp/server/fsm_open_sent.go @@ -10,7 +10,8 @@ import ( ) type openSentState struct { - fsm *FSM + fsm *FSM + peerASNRcvd uint32 } func newOpenSentState(fsm *FSM) *openSentState { @@ -73,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.options) if err != nil { switch bgperr := err.(type) { case packet.BGPError: @@ -104,6 +105,8 @@ func (s *openSentState) unexpectedMessage() (state, string) { func (s *openSentState) openMsgReceived(msg *packet.BGPMessage) (state, string) { openMsg := msg.Body.(*packet.BGPOpen) + s.peerASNRcvd = uint32(openMsg.ASN) + s.fsm.neighborID = openMsg.BGPIdentifier stopTimer(s.fsm.connectRetryTimer) if s.fsm.peer.collisionHandling(s.fsm) { @@ -114,6 +117,10 @@ func (s *openSentState) openMsgReceived(msg *packet.BGPMessage) (state, string) return s.tcpFailure() } + return s.handleOpenMessage(openMsg) +} + +func (s *openSentState) handleOpenMessage(openMsg *packet.BGPOpen) (state, string) { s.fsm.holdTime = time.Duration(math.Min(float64(s.fsm.peer.holdTime), float64(time.Duration(openMsg.HoldTime)*time.Second))) if s.fsm.holdTime != 0 { if !s.fsm.holdTimer.Reset(s.fsm.holdTime) { @@ -123,7 +130,13 @@ func (s *openSentState) openMsgReceived(msg *packet.BGPMessage) (state, string) s.fsm.keepaliveTimer = time.NewTimer(s.fsm.keepaliveTime) } + s.peerASNRcvd = uint32(openMsg.ASN) s.processOpenOptions(openMsg.OptParams) + + if s.peerASNRcvd != s.fsm.peer.peerASN { + return newCeaseState(), fmt.Sprintf("Expected session from %d, got open message with ASN %d", s.fsm.peer.peerASN, s.peerASNRcvd) + } + return newOpenConfirmState(s.fsm), "Received OPEN message" } @@ -153,7 +166,8 @@ func (s *openSentState) processCapability(cap packet.Capability) { switch cap.Code { case packet.AddPathCapabilityCode: s.processAddPathCapability(cap.Value.(packet.AddPathCapability)) - + case packet.ASN4CapabilityCode: + s.processASN4Capability(cap.Value.(packet.ASN4Capability)) } } @@ -184,6 +198,14 @@ func (s *openSentState) processAddPathCapability(addPathCap packet.AddPathCapabi } } +func (s *openSentState) processASN4Capability(cap packet.ASN4Capability) { + s.fsm.options.Supports4OctetASN = true + + if s.peerASNRcvd == packet.ASTransASN { + s.peerASNRcvd = cap.ASN4 + } +} + func (s *openSentState) notification(msg *packet.BGPMessage) (state, string) { stopTimer(s.fsm.connectRetryTimer) s.fsm.con.Close() diff --git a/protocols/bgp/server/fsm_open_sent_test.go b/protocols/bgp/server/fsm_open_sent_test.go new file mode 100644 index 0000000000000000000000000000000000000000..39057c6480f84c21d025e335cd95b2c7b34b2b08 --- /dev/null +++ b/protocols/bgp/server/fsm_open_sent_test.go @@ -0,0 +1,87 @@ +package server + +import ( + "testing" + + "github.com/bio-routing/bio-rd/protocols/bgp/packet" + "github.com/stretchr/testify/assert" +) + +func TestOpenMsgReceived(t *testing.T) { + tests := []struct { + asn uint32 + name string + msg packet.BGPOpen + wantsCease bool + }{ + { + name: "valid open message (16bit ASN)", + asn: 12345, + msg: packet.BGPOpen{ + HoldTime: 90, + BGPIdentifier: 1, + Version: 4, + ASN: 12345, + }, + }, + { + name: "valid open message (32bit ASN)", + asn: 202739, + msg: packet.BGPOpen{ + HoldTime: 90, + BGPIdentifier: 1, + Version: 4, + ASN: 23456, + OptParmLen: 1, + OptParams: []packet.OptParam{ + { + Type: packet.CapabilitiesParamType, + Length: 6, + Value: packet.Capabilities{ + packet.Capability{ + Code: packet.ASN4CapabilityCode, + Length: 4, + Value: packet.ASN4Capability{ + ASN4: 202739, + }, + }, + }, + }, + }, + }, + }, + { + name: "open message does not match configured remote ASN", + asn: 12345, + msg: packet.BGPOpen{ + HoldTime: 90, + BGPIdentifier: 1, + Version: 4, + ASN: 54321, + }, + wantsCease: true, + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + fsm := newFSM2(&peer{ + peerASN: test.asn, + }) + + s := &openSentState{ + fsm: fsm, + } + + state, _ := s.handleOpenMessage(&test.msg) + + if test.wantsCease { + assert.IsType(t, &ceaseState{}, state, "state") + return + } + + assert.IsType(t, &openConfirmState{}, state, "state") + assert.Equal(t, test.asn, s.peerASNRcvd, "asn") + }) + } +} diff --git a/protocols/bgp/server/fsm_test.go b/protocols/bgp/server/fsm_test.go new file mode 100644 index 0000000000000000000000000000000000000000..c0b293936118c345060049cfcee77a4325861741 --- /dev/null +++ b/protocols/bgp/server/fsm_test.go @@ -0,0 +1,98 @@ +package server + +import ( + "testing" + "time" + + "github.com/bio-routing/bio-rd/protocols/bgp/packet" + "github.com/stretchr/testify/assert" +) + +func TestOpenMessage(t *testing.T) { + tests := []struct { + name string + localASN uint32 + holdTime time.Duration + routerID uint32 + expected packet.BGPOpen + }{ + { + name: "16bit ASN", + localASN: 12345, + holdTime: time.Duration(30 * time.Second), + routerID: 1, + expected: packet.BGPOpen{ + ASN: 12345, + BGPIdentifier: 1, + HoldTime: 30, + OptParams: []packet.OptParam{ + packet.OptParam{ + Type: packet.CapabilitiesParamType, + Value: packet.Capabilities{ + packet.Capability{ + Code: 65, + Value: packet.ASN4Capability{ + ASN4: 12345, + }, + }, + }, + }, + }, + Version: 4, + }, + }, + { + name: "32bit ASN", + localASN: 202739, + holdTime: time.Duration(30 * time.Second), + routerID: 1, + expected: packet.BGPOpen{ + ASN: 23456, + BGPIdentifier: 1, + HoldTime: 30, + OptParams: []packet.OptParam{ + packet.OptParam{ + Type: packet.CapabilitiesParamType, + Value: packet.Capabilities{ + packet.Capability{ + Code: 65, + Value: packet.ASN4Capability{ + ASN4: 202739, + }, + }, + }, + }, + }, + Version: 4, + }, + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + p := peer{ + localASN: test.localASN, + holdTime: test.holdTime, + routerID: test.routerID, + optOpenParams: []packet.OptParam{ + packet.OptParam{ + Type: packet.CapabilitiesParamType, + Value: packet.Capabilities{ + packet.Capability{ + Code: 65, + Value: packet.ASN4Capability{ + ASN4: test.localASN, + }, + }, + }, + }, + }, + } + + fsm := newFSM2(&p) + msg := fsm.openMessage() + + assert.Equal(t, &test.expected, msg) + }) + } +} diff --git a/protocols/bgp/server/peer.go b/protocols/bgp/server/peer.go index 3e5e6aeea408d1be12c4fd1ca38b6e62ba2af3b4..fe12b36d485879278df074456027c73f4149b910 100644 --- a/protocols/bgp/server/peer.go +++ b/protocols/bgp/server/peer.go @@ -127,24 +127,12 @@ func newPeer(c config.Peer, rib *locRIB.LocRIB, server *bgpServer) (*peer, error caps := make([]packet.Capability, 0) - addPath := uint8(0) - if c.AddPathRecv { - addPath += packet.AddPathReceive - } - if !c.AddPathSend.BestOnly { - addPath += packet.AddPathSend + addPathEnabled, addPathCap := handleAddPathCapability(c) + if addPathEnabled { + caps = append(caps, addPathCap) } - if addPath > 0 { - caps = append(caps, packet.Capability{ - Code: packet.AddPathCapabilityCode, - Value: packet.AddPathCapability{ - AFI: packet.IPv4AFI, - SAFI: packet.UnicastSAFI, - SendReceive: addPath, - }, - }) - } + caps = append(caps, asn4Capability(c)) for _, cap := range caps { p.optOpenParams = append(p.optOpenParams, packet.OptParam{ @@ -156,6 +144,38 @@ func newPeer(c config.Peer, rib *locRIB.LocRIB, server *bgpServer) (*peer, error return p, nil } +func asn4Capability(c config.Peer) packet.Capability { + return packet.Capability{ + Code: packet.ASN4CapabilityCode, + Value: packet.ASN4Capability{ + ASN4: c.LocalAS, + }, + } +} + +func handleAddPathCapability(c config.Peer) (bool, packet.Capability) { + addPath := uint8(0) + if c.AddPathRecv { + addPath += packet.AddPathReceive + } + if !c.AddPathSend.BestOnly { + addPath += packet.AddPathSend + } + + if addPath == 0 { + return false, packet.Capability{} + } + + return true, packet.Capability{ + Code: packet.AddPathCapabilityCode, + Value: packet.AddPathCapability{ + AFI: packet.IPv4AFI, + SAFI: packet.UnicastSAFI, + SendReceive: addPath, + }, + } +} + func filterOrDefault(f *filter.Filter) *filter.Filter { if f != nil { return f diff --git a/protocols/bgp/server/server.go b/protocols/bgp/server/server.go index f0354f7ed1587ffd2ec84132ec50e117edfb72bb..4015f44b78b834f9793fd8c469f1c0fd6d25e6df 100644 --- a/protocols/bgp/server/server.go +++ b/protocols/bgp/server/server.go @@ -15,7 +15,6 @@ import ( ) const ( - uint16max = 65535 BGPVersion = 4 ) @@ -115,10 +114,6 @@ func (b *bgpServer) incomingConnectionWorker() { } func (b *bgpServer) AddPeer(c config.Peer, rib *locRIB.LocRIB) error { - if c.LocalAS > uint16max || c.PeerAS > uint16max { - return fmt.Errorf("32bit ASNs are not supported yet") - } - peer, err := newPeer(c, rib, b) if err != nil { return err diff --git a/protocols/bgp/server/update_helper.go b/protocols/bgp/server/update_helper.go index e0a24a6e3bbf1369b59b25d04f1f6c3c16930844..c45181803ee3c5c54f777cfbb66e7d3ad6831d53 100644 --- a/protocols/bgp/server/update_helper.go +++ b/protocols/bgp/server/update_helper.go @@ -72,11 +72,11 @@ func addOptionalPathAttribues(p *route.Path, parent *packet.PathAttribute) error } type serializeAbleUpdate interface { - SerializeUpdate() ([]byte, error) + SerializeUpdate(opt *packet.Options) ([]byte, error) } -func serializeAndSendUpdate(out io.Writer, update serializeAbleUpdate) error { - updateBytes, err := update.SerializeUpdate() +func serializeAndSendUpdate(out io.Writer, update serializeAbleUpdate, opt *packet.Options) error { + updateBytes, err := update.SerializeUpdate(opt) if err != nil { log.Errorf("Unable to serialize BGP Update: %v", err) return nil diff --git a/protocols/bgp/server/update_helper_test.go b/protocols/bgp/server/update_helper_test.go index 5752edded4aba03b66acd88950796e21c2c82620..d2d0a7fcf424276b6a351176cebc9a3959f9ec69 100644 --- a/protocols/bgp/server/update_helper_test.go +++ b/protocols/bgp/server/update_helper_test.go @@ -16,7 +16,7 @@ import ( type failingUpdate struct{} -func (f *failingUpdate) SerializeUpdate() ([]byte, error) { +func (f *failingUpdate) SerializeUpdate(opt *packet.Options) ([]byte, error) { return nil, errors.New("general error") } @@ -94,12 +94,12 @@ func TestSerializeAndSendUpdate(t *testing.T) { } for _, test := range tests { t.Run(test.name, func(t *testing.T) { - err := serializeAndSendUpdate(test.buf, test.testUpdate) + opt := &packet.Options{} + err := serializeAndSendUpdate(test.buf, test.testUpdate, opt) assert.Equal(t, test.err, err) assert.Equal(t, test.expected, test.buf.Bytes()) }) - } } diff --git a/protocols/bgp/server/update_sender.go b/protocols/bgp/server/update_sender.go index 67fb6662febaec64942dd48c6164ad00b0441bc4..a6ae74afeb29d7e5c3b4ec9538a6ecc18ffce32f 100644 --- a/protocols/bgp/server/update_sender.go +++ b/protocols/bgp/server/update_sender.go @@ -42,12 +42,12 @@ func (u *UpdateSender) AddPath(pfx net.Prefix, p *route.Path) error { }, } - return serializeAndSendUpdate(u.fsm.con, update) + return serializeAndSendUpdate(u.fsm.con, update, u.fsm.options) } // RemovePath withdraws prefix `pfx` from a peer func (u *UpdateSender) RemovePath(pfx net.Prefix, p *route.Path) bool { - err := withDrawPrefixes(u.fsm.con, pfx) + err := withDrawPrefixes(u.fsm.con, u.fsm.options, pfx) return err == nil } diff --git a/protocols/bgp/server/update_sender_add_path.go b/protocols/bgp/server/update_sender_add_path.go index 7abef161e4f5accd6b49b423849fbbd3d4a56742..afdb71f31ba31618bed84f175478cc95f18f850f 100644 --- a/protocols/bgp/server/update_sender_add_path.go +++ b/protocols/bgp/server/update_sender_add_path.go @@ -39,12 +39,12 @@ func (u *UpdateSenderAddPath) AddPath(pfx net.Prefix, p *route.Path) error { Pfxlen: pfx.Pfxlen(), }, } - return serializeAndSendUpdate(u.fsm.con, update) + return serializeAndSendUpdate(u.fsm.con, update, u.fsm.options) } // RemovePath withdraws prefix `pfx` from a peer func (u *UpdateSenderAddPath) RemovePath(pfx net.Prefix, p *route.Path) bool { - err := withDrawPrefixesAddPath(u.fsm.con, pfx, p) + err := withDrawPrefixesAddPath(u.fsm.con, u.fsm.options, pfx, p) return err == nil } diff --git a/protocols/bgp/server/withdraw.go b/protocols/bgp/server/withdraw.go index 42e97a399cdcf8c208fc29063f7213e4d780ad96..089f9f2c0f7a0e25d1c5d1f2e9f292cdeec74a3c 100644 --- a/protocols/bgp/server/withdraw.go +++ b/protocols/bgp/server/withdraw.go @@ -11,7 +11,7 @@ import ( // withDrawPrefixes generates a BGPUpdate message and write it to the given // io.Writer. -func withDrawPrefixes(out io.Writer, prefixes ...net.Prefix) error { +func withDrawPrefixes(out io.Writer, opt *packet.Options, prefixes ...net.Prefix) error { if len(prefixes) < 1 { return nil } @@ -35,13 +35,13 @@ func withDrawPrefixes(out io.Writer, prefixes ...net.Prefix) error { update := &packet.BGPUpdate{ WithdrawnRoutes: rootNLRI, } - return serializeAndSendUpdate(out, update) + return serializeAndSendUpdate(out, update, opt) } // withDrawPrefixesAddPath generates a BGPUpdateAddPath message and write it to the given // io.Writer. -func withDrawPrefixesAddPath(out io.Writer, pfx net.Prefix, p *route.Path) error { +func withDrawPrefixesAddPath(out io.Writer, opt *packet.Options, pfx net.Prefix, p *route.Path) error { if p.Type != route.BGPPathType { return errors.New("wrong path type, expected BGPPathType") } @@ -55,5 +55,5 @@ func withDrawPrefixesAddPath(out io.Writer, pfx net.Prefix, p *route.Path) error Pfxlen: pfx.Pfxlen(), }, } - return serializeAndSendUpdate(out, update) + return serializeAndSendUpdate(out, update, opt) } diff --git a/protocols/bgp/server/withdraw_test.go b/protocols/bgp/server/withdraw_test.go index 0df94f75259aa446af0d78d1a65aa9ea7a4c67b2..d8831c3eb526d3afdaceb0a3506bf5dca39c2c41 100644 --- a/protocols/bgp/server/withdraw_test.go +++ b/protocols/bgp/server/withdraw_test.go @@ -3,6 +3,8 @@ package server import ( "testing" + "github.com/bio-routing/bio-rd/protocols/bgp/packet" + "errors" "bytes" @@ -52,7 +54,8 @@ func TestWithDrawPrefixes(t *testing.T) { } for _, tc := range testcases { buf := bytes.NewBuffer([]byte{}) - err := withDrawPrefixes(buf, tc.Prefix...) + opt := &packet.Options{} + err := withDrawPrefixes(buf, opt, tc.Prefix...) assert.Equal(t, tc.ExpectedError, err, "error mismatch in testcase %v", tc.Name) assert.Equal(t, tc.Expected, buf.Bytes(), "expected different bytes in testcase %v", tc.Name) } @@ -108,7 +111,8 @@ func TestWithDrawPrefixesAddPath(t *testing.T) { } for _, tc := range testcases { buf := bytes.NewBuffer([]byte{}) - err := withDrawPrefixesAddPath(buf, tc.Prefix, tc.Path) + opt := &packet.Options{} + err := withDrawPrefixesAddPath(buf, opt, tc.Prefix, tc.Path) assert.Equal(t, tc.ExpectedError, err, "error mismatch in testcase %v", tc.Name) assert.Equal(t, tc.Expected, buf.Bytes(), "expected different bytes in testcase %v", tc.Name) }