diff --git a/protocols/bgp/packet/encoder.go b/protocols/bgp/packet/encoder.go index de2966bfeb404c70828a72874b7f1f6788fc974c..a75e2441b3e499f9f8662ba0bb793d8e9e45d9eb 100644 --- a/protocols/bgp/packet/encoder.go +++ b/protocols/bgp/packet/encoder.go @@ -7,6 +7,10 @@ import ( "github.com/taktv6/tflow2/convert" ) +type EncodingOptions struct { + Supports4OctetASN bool +} + func SerializeKeepaliveMsg() []byte { keepaliveLen := uint16(19) buf := bytes.NewBuffer(make([]byte, 0, keepaliveLen)) @@ -63,7 +67,7 @@ func serializeHeader(buf *bytes.Buffer, length uint16, typ uint8) { buf.WriteByte(typ) } -func (b *BGPUpdateAddPath) SerializeUpdate() ([]byte, error) { +func (b *BGPUpdateAddPath) SerializeUpdate(opt *EncodingOptions) ([]byte, error) { budget := MaxLen - MinLen buf := bytes.NewBuffer(nil) @@ -78,7 +82,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 +126,7 @@ func (b *BGPUpdateAddPath) SerializeUpdate() ([]byte, error) { return buf.Bytes(), nil } -func (b *BGPUpdate) SerializeUpdate() ([]byte, error) { +func (b *BGPUpdate) SerializeUpdate(opt *EncodingOptions) ([]byte, error) { budget := MaxLen - MinLen buf := bytes.NewBuffer(nil) @@ -137,7 +141,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/path_attributes.go b/protocols/bgp/packet/path_attributes.go index d040718eaaf6aae2286938f6a3d97c5a039a7daf..0cee22686658fbbafb9bc8868315a26dc8a7a8a1 100644 --- a/protocols/bgp/packet/path_attributes.go +++ b/protocols/bgp/packet/path_attributes.go @@ -409,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 *EncodingOptions) 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: @@ -447,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 *EncodingOptions) 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 5b816cc49aefe865f9afcebdfbe0e359793e3cbe..5889b913263f8fb4913ca18d3ea376365d7be8b4 100644 --- a/protocols/bgp/packet/path_attributes_test.go +++ b/protocols/bgp/packet/path_attributes_test.go @@ -1270,6 +1270,7 @@ func TestSerializeASPath(t *testing.T) { input *PathAttribute expected []byte expectedLen uint8 + use32BitASN bool }{ { name: "Test #1", @@ -1296,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 := &EncodingOptions{ + 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()) + }) } } @@ -1592,7 +1625,8 @@ func TestSerialize(t *testing.T) { } for _, test := range tests { - res, err := test.msg.SerializeUpdate() + opt := &EncodingOptions{} + res, err := test.msg.SerializeUpdate(opt) if err != nil { if test.wantFail { continue @@ -1798,7 +1832,8 @@ func TestSerializeAddPath(t *testing.T) { } for _, test := range tests { - res, err := test.msg.SerializeUpdate() + opt := &EncodingOptions{} + res, err := test.msg.SerializeUpdate(opt) if err != nil { if test.wantFail { continue diff --git a/protocols/bgp/server/fsm.go b/protocols/bgp/server/fsm.go index 108f609c84085f0308832852dc3cfd5d27fa8de2..ab2eff08cf4d2e20f16f56f9b5890f9c36cf5aa8 100644 --- a/protocols/bgp/server/fsm.go +++ b/protocols/bgp/server/fsm.go @@ -57,6 +57,7 @@ type FSM struct { capAddPathRecv bool decodingOptions *packet.DecodingOptions + encodingOptions *packet.EncodingOptions local net.IP @@ -102,6 +103,7 @@ func newFSM2(peer *peer) *FSM { stopMsgRecvCh: make(chan struct{}), rib: peer.rib, decodingOptions: &packet.DecodingOptions{}, + encodingOptions: &packet.EncodingOptions{}, } } diff --git a/protocols/bgp/server/fsm_open_sent.go b/protocols/bgp/server/fsm_open_sent.go index afbad880c40381989075e30947b63d1d03031188..c885d1294256f1181333ba790697369f0685bacd 100644 --- a/protocols/bgp/server/fsm_open_sent.go +++ b/protocols/bgp/server/fsm_open_sent.go @@ -200,6 +200,7 @@ func (s *openSentState) processAddPathCapability(addPathCap packet.AddPathCapabi func (s *openSentState) processASN4Capability(cap packet.ASN4Capability) { s.fsm.decodingOptions.Supports4OctetASN = true + s.fsm.encodingOptions.Supports4OctetASN = true if s.peerASNRcvd == packet.ASTransASN { s.peerASNRcvd = cap.ASN4 diff --git a/protocols/bgp/server/update_helper.go b/protocols/bgp/server/update_helper.go index 34f784a1de6f22cae74839dbf348865e58472c06..02a89bee791ca93aa03b5c22d778a2c320da2e07 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.EncodingOptions) ([]byte, error) } -func serializeAndSendUpdate(out io.Writer, update serializeAbleUpdate) error { - updateBytes, err := update.SerializeUpdate() +func serializeAndSendUpdate(out io.Writer, update serializeAbleUpdate, opt *packet.EncodingOptions) 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..7ce7a9a6f43f9ca6a129311ffd968d96d3ae65c7 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.EncodingOptions) ([]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.EncodingOptions{} + 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..0a0f51ae4c3b1f36288acdeb717e869d7ef8ef44 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.encodingOptions) } // 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.encodingOptions, 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..6930465540710f3d5a778826bf89141940437f16 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.encodingOptions) } // 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.encodingOptions, pfx, p) return err == nil } diff --git a/protocols/bgp/server/withdraw.go b/protocols/bgp/server/withdraw.go index 42e97a399cdcf8c208fc29063f7213e4d780ad96..90c8fb548b24fc3283c4da0fd11c1bb1be65139f 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.EncodingOptions, 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.EncodingOptions, 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..b4b592ef1ea6f94a588a0378b2723309fed0b202 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.EncodingOptions{} + 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.EncodingOptions{} + 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) }