From 9f005b373e89e84bc921a634db1628a105932af5 Mon Sep 17 00:00:00 2001
From: Daniel Czerwonk <daniel@dan-nrw.de>
Date: Sun, 24 Jun 2018 12:59:34 +0200
Subject: [PATCH] 32 asn path serialization

---
 protocols/bgp/packet/encoder.go               | 12 +++--
 protocols/bgp/packet/path_attributes.go       | 21 ++++++--
 protocols/bgp/packet/path_attributes_test.go  | 53 +++++++++++++++----
 protocols/bgp/server/fsm.go                   |  2 +
 protocols/bgp/server/fsm_open_sent.go         |  1 +
 protocols/bgp/server/update_helper.go         |  6 +--
 protocols/bgp/server/update_helper_test.go    |  6 +--
 protocols/bgp/server/update_sender.go         |  4 +-
 .../bgp/server/update_sender_add_path.go      |  4 +-
 protocols/bgp/server/withdraw.go              |  8 +--
 protocols/bgp/server/withdraw_test.go         |  8 ++-
 11 files changed, 91 insertions(+), 34 deletions(-)

diff --git a/protocols/bgp/packet/encoder.go b/protocols/bgp/packet/encoder.go
index de2966bf..a75e2441 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 d040718e..0cee2268 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 5b816cc4..5889b913 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 108f609c..ab2eff08 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 afbad880..c885d129 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 34f784a1..02a89bee 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 5752edde..7ce7a9a6 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 67fb6662..0a0f51ae 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 7abef161..69304655 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 42e97a39..90c8fb54 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 0df94f75..b4b592ef 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)
 	}
-- 
GitLab