diff --git a/protocols/bgp/packet/path_attributes.go b/protocols/bgp/packet/path_attributes.go
index 8be484780ee571432d159e63e7b2d4fcf86d2328..748e36535f049ddb31955e8e86078aef209641cd 100644
--- a/protocols/bgp/packet/path_attributes.go
+++ b/protocols/bgp/packet/path_attributes.go
@@ -342,6 +342,18 @@ func (pa *PathAttribute) setLength(buf *bytes.Buffer) (int, error) {
 	return bytesRead, nil
 }
 
+func (pa *PathAttribute) Copy() *PathAttribute {
+	return &PathAttribute{
+		ExtendedLength: pa.ExtendedLength,
+		Length:         pa.Length,
+		Optional:       pa.Optional,
+		Partial:        pa.Partial,
+		Transitive:     pa.Transitive,
+		TypeCode:       pa.TypeCode,
+		Value:          pa.Value,
+	}
+}
+
 // dumpNBytes is used to dump n bytes of buf. This is useful in case an path attributes
 // length doesn't match a fixed length's attributes length (e.g. ORIGIN is always an octet)
 func dumpNBytes(buf *bytes.Buffer, n uint16) error {
@@ -378,6 +390,8 @@ func (pa *PathAttribute) serialize(buf *bytes.Buffer, opt *Options) uint8 {
 		pathAttrLen = pa.serializeCommunities(buf)
 	case LargeCommunitiesAttr:
 		pathAttrLen = pa.serializeLargeCommunities(buf)
+	default:
+		pathAttrLen = pa.serializeUnknownAttribute(buf)
 	}
 
 	return pathAttrLen
@@ -534,6 +548,23 @@ func (pa *PathAttribute) serializeLargeCommunities(buf *bytes.Buffer) uint8 {
 	return length
 }
 
+func (pa *PathAttribute) serializeUnknownAttribute(buf *bytes.Buffer) uint8 {
+	attrFlags := uint8(0)
+	if pa.Optional {
+		attrFlags = setOptional(attrFlags)
+	}
+	attrFlags = setTransitive(attrFlags)
+
+	buf.WriteByte(attrFlags)
+	buf.WriteByte(pa.TypeCode)
+
+	b := pa.Value.([]byte)
+	buf.WriteByte(uint8(len(b)))
+	buf.Write(b)
+
+	return uint8(len(b) + 2)
+}
+
 func fourBytesToUint32(address [4]byte) uint32 {
 	return uint32(address[0])<<24 + uint32(address[1])<<16 + uint32(address[2])<<8 + uint32(address[3])
 }
diff --git a/protocols/bgp/packet/path_attributes_test.go b/protocols/bgp/packet/path_attributes_test.go
index 96200f4dc8f355da9b640388b097a3a2a9cc5062..fbf1d4806e127b1b2e4b15255a11e018bd00bbaf 100644
--- a/protocols/bgp/packet/path_attributes_test.go
+++ b/protocols/bgp/packet/path_attributes_test.go
@@ -1345,6 +1345,41 @@ func TestSerializeCommunities(t *testing.T) {
 	}
 }
 
+func TestSerializeUnknownAttribute(t *testing.T) {
+	tests := []struct {
+		name        string
+		input       *PathAttribute
+		expected    []byte
+		expectedLen uint8
+	}{
+		{
+			name: "Arbritary attribute",
+			input: &PathAttribute{
+				TypeCode:   200,
+				Value:      []byte{1, 2, 3, 4},
+				Transitive: true,
+			},
+			expected: []byte{
+				64,         // Attribute flags
+				200,        // Type
+				4,          // Length
+				1, 2, 3, 4, // Payload
+			},
+			expectedLen: 6,
+		},
+	}
+
+	for _, test := range tests {
+		t.Run(test.name, func(t *testing.T) {
+			buf := bytes.NewBuffer(nil)
+			n := test.input.serializeUnknownAttribute(buf)
+
+			assert.Equal(t, test.expectedLen, n)
+			assert.Equal(t, test.expected, buf.Bytes())
+		})
+	}
+}
+
 func TestSerialize(t *testing.T) {
 	tests := []struct {
 		name     string
diff --git a/protocols/bgp/server/fsm_established.go b/protocols/bgp/server/fsm_established.go
index 3313923e6c7692aa8035d1ab697d3fa6e2e21c13..fdb6ea30a1463ad06e0cb44507205eed71d7c75b 100644
--- a/protocols/bgp/server/fsm_established.go
+++ b/protocols/bgp/server/fsm_established.go
@@ -221,29 +221,55 @@ func (s *establishedState) updates(u *packet.BGPUpdate) {
 			},
 		}
 
-		for pa := u.PathAttributes; pa != nil; pa = pa.Next {
-			switch pa.TypeCode {
-			case packet.OriginAttr:
-				path.BGPPath.Origin = pa.Value.(uint8)
-			case packet.LocalPrefAttr:
-				path.BGPPath.LocalPref = pa.Value.(uint32)
-			case packet.MEDAttr:
-				path.BGPPath.MED = pa.Value.(uint32)
-			case packet.NextHopAttr:
-				path.BGPPath.NextHop = pa.Value.(uint32)
-			case packet.ASPathAttr:
-				path.BGPPath.ASPath = pa.Value.(packet.ASPath)
-				path.BGPPath.ASPathLen = path.BGPPath.ASPath.Length()
-			case packet.CommunitiesAttr:
-				path.BGPPath.Communities = pa.Value.([]uint32)
-			case packet.LargeCommunitiesAttr:
-				path.BGPPath.LargeCommunities = pa.Value.([]packet.LargeCommunity)
+		s.processAttributes(u.PathAttributes, path)
+
+		s.fsm.adjRIBIn.AddPath(pfx, path)
+	}
+}
+
+func (s *establishedState) processAttributes(attrs *packet.PathAttribute, path *route.Path) {
+	var currentUnknown *packet.PathAttribute
+
+	for pa := attrs; pa != nil; pa = pa.Next {
+		switch pa.TypeCode {
+		case packet.OriginAttr:
+			path.BGPPath.Origin = pa.Value.(uint8)
+		case packet.LocalPrefAttr:
+			path.BGPPath.LocalPref = pa.Value.(uint32)
+		case packet.MEDAttr:
+			path.BGPPath.MED = pa.Value.(uint32)
+		case packet.NextHopAttr:
+			path.BGPPath.NextHop = pa.Value.(uint32)
+		case packet.ASPathAttr:
+			path.BGPPath.ASPath = pa.Value.(packet.ASPath)
+			path.BGPPath.ASPathLen = path.BGPPath.ASPath.Length()
+		case packet.CommunitiesAttr:
+			path.BGPPath.Communities = pa.Value.([]uint32)
+		case packet.LargeCommunitiesAttr:
+			path.BGPPath.LargeCommunities = pa.Value.([]packet.LargeCommunity)
+		default:
+			currentUnknown = s.processUnknownAttribute(pa, currentUnknown)
+			if path.BGPPath.UnknownAttributes == nil {
+				path.BGPPath.UnknownAttributes = currentUnknown
 			}
 		}
-		s.fsm.adjRIBIn.AddPath(pfx, path)
 	}
 }
 
+func (s *establishedState) processUnknownAttribute(attr, current *packet.PathAttribute) *packet.PathAttribute {
+	if !attr.Transitive {
+		return current
+	}
+
+	p := attr.Copy()
+	if current == nil {
+		return p
+	}
+
+	current.Next = p
+	return p
+}
+
 func (s *establishedState) keepaliveReceived() (state, string) {
 	if s.fsm.holdTime != 0 {
 		s.fsm.holdTimer.Reset(s.fsm.holdTime)
diff --git a/protocols/bgp/server/fsm_established_test.go b/protocols/bgp/server/fsm_established_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..4e92e4eede30ed4b0dfedeb1217e5d11962c6252
--- /dev/null
+++ b/protocols/bgp/server/fsm_established_test.go
@@ -0,0 +1,65 @@
+package server
+
+import (
+	"testing"
+
+	"github.com/bio-routing/bio-rd/protocols/bgp/packet"
+	"github.com/bio-routing/bio-rd/route"
+	"github.com/stretchr/testify/assert"
+)
+
+func TestProcessAttribues(t *testing.T) {
+	unknown3 := &packet.PathAttribute{
+		Transitive: true,
+		TypeCode:   100,
+		Value:      []byte{1, 2, 3, 4},
+		Next:       nil,
+	}
+
+	unknown2 := &packet.PathAttribute{
+		Transitive: false,
+		TypeCode:   150,
+		Value:      []byte{20},
+		Next:       unknown3,
+	}
+
+	unknown1 := &packet.PathAttribute{
+		Transitive: true,
+		TypeCode:   200,
+		Value:      []byte{5, 6},
+		Next:       unknown2,
+	}
+
+	asPath := &packet.PathAttribute{
+		Transitive: true,
+		TypeCode:   packet.ASPathAttr,
+		Value: packet.ASPath{
+			packet.ASPathSegment{
+				Count: 0,
+				Type:  packet.ASSequence,
+				ASNs:  []uint32{},
+			},
+		},
+		Next: unknown1,
+	}
+
+	e := &establishedState{}
+
+	p := &route.Path{
+		BGPPath: &route.BGPPath{},
+	}
+	e.processAttributes(asPath, p)
+
+	expectedCodes := []uint8{200, 100}
+	expectedValues := [][]byte{[]byte{5, 6}, []byte{1, 2, 3, 4}}
+
+	i := 0
+	for attr := p.BGPPath.UnknownAttributes; attr != nil; attr = attr.Next {
+		assert.Equal(t, true, attr.Transitive, "Transitive")
+		assert.Equal(t, expectedCodes[i], attr.TypeCode, "Code")
+		assert.Equal(t, expectedValues[i], attr.Value, "Value")
+		i++
+	}
+
+	assert.Equal(t, i, 2, "Count")
+}
diff --git a/protocols/bgp/server/update_helper.go b/protocols/bgp/server/update_helper.go
index 50850cb6e3747f95bee8dd802797269bb3d63724..1fe4fdbaf05d6e483047712861874365a3af74a3 100644
--- a/protocols/bgp/server/update_helper.go
+++ b/protocols/bgp/server/update_helper.go
@@ -34,17 +34,14 @@ func pathAttribues(p *route.Path) (*packet.PathAttribute, error) {
 	nextHop.Next = localPref
 
 	if p.BGPPath != nil {
-		err := addOptionalPathAttribues(p, localPref)
-
-		if err != nil {
-			return nil, err
-		}
+		optionals := addOptionalPathAttribues(p, localPref)
+		optionals.Next = p.BGPPath.UnknownAttributes
 	}
 
 	return asPath, nil
 }
 
-func addOptionalPathAttribues(p *route.Path, parent *packet.PathAttribute) error {
+func addOptionalPathAttribues(p *route.Path, parent *packet.PathAttribute) *packet.PathAttribute {
 	current := parent
 
 	if len(p.BGPPath.Communities) > 0 {
@@ -65,7 +62,7 @@ func addOptionalPathAttribues(p *route.Path, parent *packet.PathAttribute) error
 		current = largeCommunities
 	}
 
-	return nil
+	return current
 }
 
 type serializeAbleUpdate interface {
diff --git a/route/bgp_path.go b/route/bgp_path.go
index 509d39844ac25236eef26071052ee5410ec3921f..baac670a2c526fd7777a7dd1d0114b4b0a6399f1 100644
--- a/route/bgp_path.go
+++ b/route/bgp_path.go
@@ -11,18 +11,19 @@ import (
 
 // BGPPath represents a set of BGP path attributes
 type BGPPath struct {
-	PathIdentifier   uint32
-	NextHop          uint32
-	LocalPref        uint32
-	ASPath           packet.ASPath
-	ASPathLen        uint16
-	Origin           uint8
-	MED              uint32
-	EBGP             bool
-	BGPIdentifier    uint32
-	Source           uint32
-	Communities      []uint32
-	LargeCommunities []packet.LargeCommunity
+	PathIdentifier    uint32
+	NextHop           uint32
+	LocalPref         uint32
+	ASPath            packet.ASPath
+	ASPathLen         uint16
+	Origin            uint8
+	MED               uint32
+	EBGP              bool
+	BGPIdentifier     uint32
+	Source            uint32
+	Communities       []uint32
+	LargeCommunities  []packet.LargeCommunity
+	UnknownAttributes *packet.PathAttribute
 }
 
 // ECMP determines if routes b and c are euqal in terms of ECMP