diff --git a/protocols/bgp/packet/BUILD.bazel b/protocols/bgp/packet/BUILD.bazel
index 66762a51dfcce62cd24b01dbc3d288dd7c26fbcf..49f16b7773345b729ff25a1a486a86b1669992ed 100644
--- a/protocols/bgp/packet/BUILD.bazel
+++ b/protocols/bgp/packet/BUILD.bazel
@@ -3,6 +3,7 @@ load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test")
 go_library(
     name = "go_default_library",
     srcs = [
+        "as_path.go",
         "bgp.go",
         "community.go",
         "decoder.go",
diff --git a/protocols/bgp/packet/as_path.go b/protocols/bgp/packet/as_path.go
new file mode 100644
index 0000000000000000000000000000000000000000..0cdfc96a1b6f418013ae6a6656f6cc8ecf554686
--- /dev/null
+++ b/protocols/bgp/packet/as_path.go
@@ -0,0 +1,44 @@
+package packet
+
+import "fmt"
+
+type ASPath []ASPathSegment
+
+type ASPathSegment struct {
+	Type  uint8
+	Count uint8
+	ASNs  []uint32
+}
+
+func (pa ASPath) String() (ret string) {
+	for _, p := range pa {
+		if p.Type == ASSet {
+			ret += " ("
+		}
+		n := len(p.ASNs)
+		for i, asn := range p.ASNs {
+			if i < n-1 {
+				ret += fmt.Sprintf("%d ", asn)
+				continue
+			}
+			ret += fmt.Sprintf("%d", asn)
+		}
+		if p.Type == ASSet {
+			ret += ")"
+		}
+	}
+
+	return
+}
+
+func (pa ASPath) Length() (ret uint16) {
+	for _, p := range pa {
+		if p.Type == ASSet {
+			ret++
+			continue
+		}
+		ret += uint16(len(p.ASNs))
+	}
+
+	return
+}
diff --git a/protocols/bgp/packet/bgp.go b/protocols/bgp/packet/bgp.go
index 8082f5dd8c02ae2c74a23013bcdb4c63d524d52b..8aabec7f7c729cd23d47ed6abe517742f2583708 100644
--- a/protocols/bgp/packet/bgp.go
+++ b/protocols/bgp/packet/bgp.go
@@ -173,13 +173,6 @@ type NLRIAddPath struct {
 	Next           *NLRIAddPath
 }
 
-type ASPath []ASPathSegment
-type ASPathSegment struct {
-	Type  uint8
-	Count uint8
-	ASNs  []uint32
-}
-
 type Aggretator struct {
 	Addr uint32
 	ASN  uint16
diff --git a/protocols/bgp/packet/large_community.go b/protocols/bgp/packet/large_community.go
index 5b3f7788b16c921f114880ac36d04e8868938627..4d93eb600bff85f3899e3a3df8246002f903dfb4 100644
--- a/protocols/bgp/packet/large_community.go
+++ b/protocols/bgp/packet/large_community.go
@@ -12,7 +12,7 @@ type LargeCommunity struct {
 	DataPart2           uint32
 }
 
-func (c LargeCommunity) String() string {
+func (c *LargeCommunity) String() string {
 	return fmt.Sprintf("(%d,%d,%d)", c.GlobalAdministrator, c.DataPart1, c.DataPart2)
 }
 
diff --git a/protocols/bgp/packet/path_attributes.go b/protocols/bgp/packet/path_attributes.go
index acd5c78ab813c5b3cc6442f61067c85471366545..8be484780ee571432d159e63e7b2d4fcf86d2328 100644
--- a/protocols/bgp/packet/path_attributes.go
+++ b/protocols/bgp/packet/path_attributes.go
@@ -3,8 +3,6 @@ package packet
 import (
 	"bytes"
 	"fmt"
-	"strconv"
-	"strings"
 
 	"github.com/taktv6/tflow2/convert"
 )
@@ -344,57 +342,6 @@ func (pa *PathAttribute) setLength(buf *bytes.Buffer) (int, error) {
 	return bytesRead, nil
 }
 
-func (pa *PathAttribute) ASPathString() (ret string) {
-	for _, p := range pa.Value.(ASPath) {
-		if p.Type == ASSet {
-			ret += " ("
-		}
-		n := len(p.ASNs)
-		for i, asn := range p.ASNs {
-			if i < n-1 {
-				ret += fmt.Sprintf("%d ", asn)
-				continue
-			}
-			ret += fmt.Sprintf("%d", asn)
-		}
-		if p.Type == ASSet {
-			ret += ")"
-		}
-	}
-
-	return
-}
-
-func (pa *PathAttribute) ASPathLen() (ret uint16) {
-	for _, p := range pa.Value.(ASPath) {
-		if p.Type == ASSet {
-			ret++
-			continue
-		}
-		ret += uint16(len(p.ASNs))
-	}
-
-	return
-}
-
-func (a *PathAttribute) CommunityString() string {
-	s := ""
-	for _, com := range a.Value.([]uint32) {
-		s += CommunityStringForUint32(com) + " "
-	}
-
-	return strings.TrimRight(s, " ")
-}
-
-func (a *PathAttribute) LargeCommunityString() string {
-	s := ""
-	for _, com := range a.Value.([]LargeCommunity) {
-		s += com.String() + " "
-	}
-
-	return strings.TrimRight(s, " ")
-}
-
 // 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 {
@@ -587,135 +534,6 @@ func (pa *PathAttribute) serializeLargeCommunities(buf *bytes.Buffer) uint8 {
 	return length
 }
 
-/*func (pa *PathAttribute) PrependASPath(prepend []uint32) {
-	if pa.TypeCode != ASPathAttr {
-		return
-	}
-
-	asPath := pa.Value.(ASPath)
-	asPathSegementCount := len(asPath)
-	currentSegment := asPathSegementCount - 1
-
-	newSegmentNeeded := false
-	if asPath[asPathSegementCount-1].Type == ASSequence {
-		newSegmentNeeded = true
-	} else {
-		if len(asPath[asPathSegementCount-1].ASNs) >= MaxASNsSegment {
-			newSegmentNeeded = true
-		}
-	}
-
-	for _, asn := range prepend {
-		if newSegmentNeeded {
-			segment := ASPathSegment{
-				Type: ASSequence,
-				ASNs: make([]uint32, 0),
-			},
-		}
-
-		asPath[currentSegment].ASNs = append(asPath[currentSegment].ASNs, asn)
-		if len(asPath[asPathSegementCount-1].ASNs) >= MaxASNsSegment {
-			newSegmentNeeded = true
-		}
-	}
-
-}*/
-
-// ParseASPathStr converts an AS path from string representation info an PathAttribute object
-func ParseASPathStr(asPathString string) (*PathAttribute, error) {
-	asPath := ASPath{}
-
-	currentType := ASSequence
-	newSegmentNeeded := true
-	currentSegment := -1
-	for _, asn := range strings.Split(asPathString, " ") {
-		if asn == "" {
-			continue
-		}
-
-		if isBeginOfASSet(asn) {
-			currentType = ASSet
-			newSegmentNeeded = true
-			asn = strings.Replace(asn, "(", "", 1)
-		}
-
-		if newSegmentNeeded {
-			seg := ASPathSegment{
-				Type: uint8(currentType),
-				ASNs: make([]uint32, 0),
-			}
-			asPath = append(asPath, seg)
-			currentSegment++
-			newSegmentNeeded = false
-		}
-
-		if isEndOfASSset(asn) {
-			currentType = ASSequence
-			newSegmentNeeded = true
-			asn = strings.Replace(asn, ")", "", 1)
-		}
-
-		numericASN, err := strconv.Atoi(asn)
-		if err != nil {
-			return nil, fmt.Errorf("Unable to convert ASN: %v", err)
-		}
-		asPath[currentSegment].ASNs = append(asPath[currentSegment].ASNs, uint32(numericASN))
-
-		if len(asPath[currentSegment].ASNs) == MaxASNsSegment {
-			newSegmentNeeded = true
-		}
-	}
-
-	return &PathAttribute{
-		TypeCode: ASPathAttr,
-		Value:    asPath,
-	}, nil
-}
-
-func LargeCommunityAttributeForString(s string) (*PathAttribute, error) {
-	strs := strings.Split(s, " ")
-	coms := make([]LargeCommunity, len(strs))
-
-	var err error
-	for i, str := range strs {
-		coms[i], err = ParseLargeCommunityString(str)
-		if err != nil {
-			return nil, err
-		}
-	}
-
-	return &PathAttribute{
-		TypeCode: LargeCommunitiesAttr,
-		Value:    coms,
-	}, nil
-}
-
-func CommunityAttributeForString(s string) (*PathAttribute, error) {
-	strs := strings.Split(s, " ")
-	coms := make([]uint32, len(strs))
-
-	var err error
-	for i, str := range strs {
-		coms[i], err = ParseCommunityString(str)
-		if err != nil {
-			return nil, err
-		}
-	}
-
-	return &PathAttribute{
-		TypeCode: CommunitiesAttr,
-		Value:    coms,
-	}, nil
-}
-
-func isBeginOfASSet(asPathPart string) bool {
-	return strings.Contains(asPathPart, "(")
-}
-
-func isEndOfASSset(asPathPart string) bool {
-	return strings.Contains(asPathPart, ")")
-}
-
 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 c3d5ecda2e4d484ba48809651248e4ecd1c82188..96200f4dc8f355da9b640388b097a3a2a9cc5062 100644
--- a/protocols/bgp/packet/path_attributes_test.go
+++ b/protocols/bgp/packet/path_attributes_test.go
@@ -872,107 +872,6 @@ func TestDecodeUint32(t *testing.T) {
 	}
 }
 
-func TestASPathString(t *testing.T) {
-	tests := []struct {
-		name     string
-		pa       *PathAttribute
-		expected string
-	}{
-		{
-			name: "Test #1",
-			pa: &PathAttribute{
-				Value: ASPath{
-					{
-						Type: ASSequence,
-						ASNs: []uint32{10, 20, 30},
-					},
-				},
-			},
-			expected: "10 20 30",
-		},
-		{
-			name: "Test #2",
-			pa: &PathAttribute{
-				Value: ASPath{
-					{
-						Type: ASSequence,
-						ASNs: []uint32{10, 20, 30},
-					},
-					{
-						Type: ASSet,
-						ASNs: []uint32{200, 300},
-					},
-				},
-			},
-			expected: "10 20 30 (200 300)",
-		},
-	}
-
-	for _, test := range tests {
-		res := test.pa.ASPathString()
-		assert.Equal(t, test.expected, res)
-	}
-}
-
-func TestLargeCommunityString(t *testing.T) {
-	tests := []struct {
-		name     string
-		pa       *PathAttribute
-		expected string
-	}{
-		{
-			name: "two attributes",
-			pa: &PathAttribute{
-				Value: []LargeCommunity{
-					{
-						GlobalAdministrator: 1,
-						DataPart1:           2,
-						DataPart2:           3,
-					},
-					{
-						GlobalAdministrator: 4,
-						DataPart1:           5,
-						DataPart2:           6,
-					},
-				},
-			},
-			expected: "(1,2,3) (4,5,6)",
-		},
-	}
-
-	for _, test := range tests {
-		t.Run(test.name, func(te *testing.T) {
-			res := test.pa.LargeCommunityString()
-			assert.Equal(te, test.expected, res)
-		})
-	}
-}
-
-func TestCommunityString(t *testing.T) {
-	tests := []struct {
-		name     string
-		pa       *PathAttribute
-		expected string
-	}{
-		{
-			name: "two attributes",
-			pa: &PathAttribute{
-				Value: []uint32{
-					131080, 16778241,
-				},
-			},
-			expected: "(2,8) (256,1025)",
-		},
-	}
-
-	for _, test := range tests {
-		t.Run(test.name, func(te *testing.T) {
-			res := test.pa.CommunityString()
-			assert.Equal(te, test.expected, res)
-		})
-	}
-}
-
 func TestSetOptional(t *testing.T) {
 	tests := []struct {
 		name     string
@@ -1852,108 +1751,6 @@ func TestSerializeAddPath(t *testing.T) {
 	}
 }
 
-func TestParseASPathStr(t *testing.T) {
-	tests := []struct {
-		name     string
-		input    string
-		wantFail bool
-		expected *PathAttribute
-	}{
-		{
-			name:     "Empty AS Path",
-			input:    "",
-			wantFail: false,
-			expected: &PathAttribute{
-				TypeCode: ASPathAttr,
-				Value:    ASPath{},
-			},
-		},
-		{
-			name:     "Simple AS_SEQUENCE",
-			input:    "3320 15169",
-			wantFail: false,
-			expected: &PathAttribute{
-				TypeCode: ASPathAttr,
-				Value: ASPath{
-					ASPathSegment{
-						Type: ASSequence,
-						ASNs: []uint32{3320, 15169},
-					},
-				},
-			},
-		},
-		{
-			name:     "AS_SEQUENCE with more than 255 elements",
-			input:    "123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123",
-			wantFail: false,
-			expected: &PathAttribute{
-				TypeCode: ASPathAttr,
-				Value: ASPath{
-					ASPathSegment{
-						Type: ASSequence,
-						ASNs: []uint32{123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123},
-					},
-					ASPathSegment{
-						Type: ASSequence,
-						ASNs: []uint32{123, 123, 123, 123, 123},
-					},
-				},
-			},
-		},
-		{
-			name:     "AS_SET only",
-			input:    "(3320 201701 15169)",
-			wantFail: false,
-			expected: &PathAttribute{
-				TypeCode: ASPathAttr,
-				Value: ASPath{
-					ASPathSegment{
-						Type: ASSet,
-						ASNs: []uint32{3320, 201701, 15169},
-					},
-				},
-			},
-		},
-		{
-			name:     "Mixed AS Path",
-			input:    "199714 51324 (3320 201701 15169)",
-			wantFail: false,
-			expected: &PathAttribute{
-				TypeCode: ASPathAttr,
-				Value: ASPath{
-					ASPathSegment{
-						Type: ASSequence,
-						ASNs: []uint32{199714, 51324},
-					},
-					ASPathSegment{
-						Type: ASSet,
-						ASNs: []uint32{3320, 201701, 15169},
-					},
-				},
-			},
-		},
-	}
-
-	for _, test := range tests {
-		res, err := ParseASPathStr(test.input)
-		if err != nil {
-			if test.wantFail {
-				continue
-			}
-
-			t.Errorf("Unexpected failure for test %q: %v", test.name, err)
-			continue
-		}
-
-		if test.wantFail {
-			t.Errorf("Unexpected success for test %q", test.name)
-			continue
-		}
-
-		assert.Equal(t, test.expected, res)
-	}
-}
-
 func TestFourBytesToUint32(t *testing.T) {
 	tests := []struct {
 		name     string
diff --git a/protocols/bgp/server/fsm_established.go b/protocols/bgp/server/fsm_established.go
index 66480407dc7e39e174b4f391017bfdc8c9f2e05f..e74a660ab859e00b2d70f66175ddc6c750ffd495 100644
--- a/protocols/bgp/server/fsm_established.go
+++ b/protocols/bgp/server/fsm_established.go
@@ -171,6 +171,7 @@ func (s *establishedState) msgReceived(data []byte) (state, string) {
 	}
 	switch msg.Header.Type {
 	case packet.NotificationMsg:
+		fmt.Println(data)
 		return s.notification()
 	case packet.UpdateMsg:
 		return s.update(msg)
@@ -230,12 +231,12 @@ func (s *establishedState) updates(u *packet.BGPUpdate) {
 			case packet.NextHopAttr:
 				path.BGPPath.NextHop = pa.Value.(uint32)
 			case packet.ASPathAttr:
-				path.BGPPath.ASPath = pa.ASPathString()
-				path.BGPPath.ASPathLen = pa.ASPathLen()
+				path.BGPPath.ASPath = pa.Value.(packet.ASPath)
+				path.BGPPath.ASPathLen = path.BGPPath.ASPath.Length()
 			case packet.CommunitiesAttr:
-				path.BGPPath.Communities = pa.CommunityString()
+				path.BGPPath.Communities = pa.Value.([]uint32)
 			case packet.LargeCommunitiesAttr:
-				path.BGPPath.LargeCommunities = pa.LargeCommunityString()
+				path.BGPPath.LargeCommunities = pa.Value.([]packet.LargeCommunity)
 			}
 		}
 		s.fsm.adjRIBIn.AddPath(pfx, path)
diff --git a/protocols/bgp/server/update_helper.go b/protocols/bgp/server/update_helper.go
index c45181803ee3c5c54f777cfbb66e7d3ad6831d53..50850cb6e3747f95bee8dd802797269bb3d63724 100644
--- a/protocols/bgp/server/update_helper.go
+++ b/protocols/bgp/server/update_helper.go
@@ -3,7 +3,6 @@ package server
 import (
 	"fmt"
 	"io"
-	"strings"
 
 	"github.com/bio-routing/bio-rd/protocols/bgp/packet"
 	"github.com/bio-routing/bio-rd/route"
@@ -11,22 +10,22 @@ import (
 )
 
 func pathAttribues(p *route.Path) (*packet.PathAttribute, error) {
-	asPathPA, err := packet.ParseASPathStr(strings.TrimRight(p.BGPPath.ASPath, " "))
-	if err != nil {
-		return nil, fmt.Errorf("Unable to parse AS path: %v", err)
+	asPath := &packet.PathAttribute{
+		TypeCode: packet.ASPathAttr,
+		Value:    p.BGPPath.ASPath,
 	}
 
 	origin := &packet.PathAttribute{
 		TypeCode: packet.OriginAttr,
 		Value:    p.BGPPath.Origin,
-		Next:     asPathPA,
 	}
+	asPath.Next = origin
 
 	nextHop := &packet.PathAttribute{
 		TypeCode: packet.NextHopAttr,
 		Value:    p.BGPPath.NextHop,
 	}
-	asPathPA.Next = nextHop
+	origin.Next = nextHop
 
 	localPref := &packet.PathAttribute{
 		TypeCode: packet.LocalPrefAttr,
@@ -42,28 +41,26 @@ func pathAttribues(p *route.Path) (*packet.PathAttribute, error) {
 		}
 	}
 
-	return origin, nil
+	return asPath, nil
 }
 
 func addOptionalPathAttribues(p *route.Path, parent *packet.PathAttribute) error {
 	current := parent
 
 	if len(p.BGPPath.Communities) > 0 {
-		communities, err := packet.CommunityAttributeForString(p.BGPPath.Communities)
-		if err != nil {
-			return fmt.Errorf("Could not create communities attribute: %v", err)
+		communities := &packet.PathAttribute{
+			TypeCode: packet.CommunitiesAttr,
+			Value:    p.BGPPath.Communities,
 		}
-
 		current.Next = communities
 		current = communities
 	}
 
 	if len(p.BGPPath.LargeCommunities) > 0 {
-		largeCommunities, err := packet.LargeCommunityAttributeForString(p.BGPPath.LargeCommunities)
-		if err != nil {
-			return fmt.Errorf("Could not create large communities attribute: %v", err)
+		largeCommunities := &packet.PathAttribute{
+			TypeCode: packet.LargeCommunitiesAttr,
+			Value:    p.BGPPath.LargeCommunities,
 		}
-
 		current.Next = largeCommunities
 		current = largeCommunities
 	}
diff --git a/route/BUILD.bazel b/route/BUILD.bazel
index 924cd80663f4a23fc0de1b3e5dd33b6f4500eceb..43ece0720b95d36285849f9f80d9d07d2d259b6d 100644
--- a/route/BUILD.bazel
+++ b/route/BUILD.bazel
@@ -3,7 +3,7 @@ load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test")
 go_library(
     name = "go_default_library",
     srcs = [
-        "bgp.go",
+        "bgp_path.go",
         "bgp_path_manager.go",
         "path.go",
         "route.go",
@@ -13,6 +13,7 @@ go_library(
     visibility = ["//visibility:public"],
     deps = [
         "//net:go_default_library",
+        "//protocols/bgp/packet:go_default_library",
         "//vendor/github.com/taktv6/tflow2/convert:go_default_library",
     ],
 )
@@ -20,12 +21,15 @@ go_library(
 go_test(
     name = "go_default_test",
     srcs = [
+        "bgp_path_test.go",
+        "bgp_test.go",
         "path_test.go",
         "route_test.go",
     ],
     embed = [":go_default_library"],
     deps = [
         "//net:go_default_library",
+        "//protocols/bgp/packet:go_default_library",
         "//vendor/github.com/stretchr/testify/assert:go_default_library",
     ],
 )
diff --git a/route/bgp.go b/route/bgp_path.go
similarity index 60%
rename from route/bgp.go
rename to route/bgp_path.go
index a63eb2fd396f16b2ae61e6f24d863ea5fbace185..0ab7dc4b2a3f6a23d77be1a0613b68515a5d1604 100644
--- a/route/bgp.go
+++ b/route/bgp_path.go
@@ -1,10 +1,11 @@
 package route
 
 import (
+	"crypto/sha256"
 	"fmt"
-	"strconv"
 	"strings"
 
+	"github.com/bio-routing/bio-rd/protocols/bgp/packet"
 	"github.com/taktv6/tflow2/convert"
 )
 
@@ -13,15 +14,15 @@ type BGPPath struct {
 	PathIdentifier   uint32
 	NextHop          uint32
 	LocalPref        uint32
-	ASPath           string
+	ASPath           packet.ASPath
 	ASPathLen        uint16
 	Origin           uint8
 	MED              uint32
 	EBGP             bool
 	BGPIdentifier    uint32
 	Source           uint32
-	Communities      string
-	LargeCommunities string
+	Communities      []uint32
+	LargeCommunities []packet.LargeCommunity
 }
 
 // ECMP determines if routes b and c are euqal in terms of ECMP
@@ -154,14 +155,14 @@ func (b *BGPPath) Print() string {
 	}
 	ret := fmt.Sprintf("\t\tLocal Pref: %d\n", b.LocalPref)
 	ret += fmt.Sprintf("\t\tOrigin: %s\n", origin)
-	ret += fmt.Sprintf("\t\tAS Path: %s\n", b.ASPath)
+	ret += fmt.Sprintf("\t\tAS Path: %v\n", b.ASPath)
 	nh := uint32To4Byte(b.NextHop)
 	ret += fmt.Sprintf("\t\tNEXT HOP: %d.%d.%d.%d\n", nh[0], nh[1], nh[2], nh[3])
 	ret += fmt.Sprintf("\t\tMED: %d\n", b.MED)
 	ret += fmt.Sprintf("\t\tPath ID: %d\n", b.PathIdentifier)
 	ret += fmt.Sprintf("\t\tSource: %d\n", b.Source)
-	ret += fmt.Sprintf("\t\tCommunities: %s\n", b.Communities)
-	ret += fmt.Sprintf("\t\tLargeCommunities: %s\n", b.LargeCommunities)
+	ret += fmt.Sprintf("\t\tCommunities: %v\n", b.Communities)
+	ret += fmt.Sprintf("\t\tLargeCommunities: %v\n", b.LargeCommunities)
 
 	return ret
 }
@@ -171,16 +172,40 @@ func (b *BGPPath) Prepend(asn uint32, times uint16) {
 		return
 	}
 
-	asnStr := strconv.FormatUint(uint64(asn), 10)
+	if len(b.ASPath) == 0 {
+		b.insertNewASSequence()
+	}
 
-	path := make([]string, times+1)
-	for i := 0; uint16(i) < times; i++ {
-		path[i] = asnStr
+	first := b.ASPath[0]
+	if first.Type == packet.ASSet {
+		b.insertNewASSequence()
 	}
-	path[times] = b.ASPath
 
-	b.ASPath = strings.TrimSuffix(strings.Join(path, " "), " ")
-	b.ASPathLen = b.ASPathLen + times
+	for i := 0; i < int(times); i++ {
+		if len(b.ASPath) == packet.MaxASNsSegment {
+			b.insertNewASSequence()
+		}
+
+		old := b.ASPath[0].ASNs
+		asns := make([]uint32, len(old)+1)
+		copy(asns[1:], old)
+		asns[0] = asn
+		b.ASPath[0].ASNs = asns
+	}
+
+	b.ASPathLen = b.ASPath.Length()
+}
+
+func (b *BGPPath) insertNewASSequence() packet.ASPath {
+	pa := make(packet.ASPath, len(b.ASPath)+1)
+	copy(pa[1:], b.ASPath)
+	pa[0] = packet.ASPathSegment{
+		ASNs:  make([]uint32, 0),
+		Count: 0,
+		Type:  packet.ASSequence,
+	}
+
+	return pa
 }
 
 func (p *BGPPath) Copy() *BGPPath {
@@ -192,6 +217,44 @@ func (p *BGPPath) Copy() *BGPPath {
 	return &cp
 }
 
+// ComputeHash computes an hash over all attributes of the path
+func (b *BGPPath) ComputeHash() string {
+	s := fmt.Sprintf("%d\t%d\t%v\t%d\t%d\t%v\t%d\t%d\t%v\t%v\t%d",
+		b.NextHop,
+		b.LocalPref,
+		b.ASPath,
+		b.Origin,
+		b.MED,
+		b.EBGP,
+		b.BGPIdentifier,
+		b.Source,
+		b.Communities,
+		b.LargeCommunities,
+		b.PathIdentifier)
+
+	return fmt.Sprintf("%x", sha256.Sum256([]byte(s)))
+}
+
+// CommunitiesString returns the formated communities
+func (b *BGPPath) CommunitiesString() string {
+	str := ""
+	for _, com := range b.Communities {
+		str += packet.CommunityStringForUint32(com) + " "
+	}
+
+	return strings.TrimRight(str, " ")
+}
+
+// LargeCommunitiesString returns the formated communities
+func (b *BGPPath) LargeCommunitiesString() string {
+	str := ""
+	for _, com := range b.LargeCommunities {
+		str += com.String() + " "
+	}
+
+	return strings.TrimRight(str, " ")
+}
+
 func uint32To4Byte(addr uint32) [4]byte {
 	slice := convert.Uint32Byte(addr)
 	ret := [4]byte{
diff --git a/route/bgp_path_manager.go b/route/bgp_path_manager.go
index f846d3b60e108fc4be97597fabefe4ab46cb9d13..0eec9427911eced59371ea3ebf44a91bd2649342 100644
--- a/route/bgp_path_manager.go
+++ b/route/bgp_path_manager.go
@@ -7,7 +7,7 @@ import (
 
 // BGPPathManager is a component used to deduplicate BGP Path objects
 type BGPPathManager struct {
-	paths map[BGPPath]*BGPPathCounter
+	paths map[string]*BGPPathCounter
 	mu    sync.Mutex
 }
 
@@ -24,7 +24,7 @@ func NewBGPPathManager() *BGPPathManager {
 }
 
 func (m *BGPPathManager) lookup(p BGPPath) *BGPPath {
-	pathCounter, ok := m.paths[p]
+	pathCounter, ok := m.paths[p.ComputeHash()]
 	if !ok {
 		return nil
 	}
@@ -37,15 +37,17 @@ func (m *BGPPathManager) AddPath(p BGPPath) *BGPPath {
 	m.mu.Lock()
 	defer m.mu.Unlock()
 
+	hash := p.ComputeHash()
+
 	q := m.lookup(p)
 	if q == nil {
-		m.paths[p] = &BGPPathCounter{
+		m.paths[hash] = &BGPPathCounter{
 			path: &p,
 		}
 	}
 
-	m.paths[p].usageCount++
-	return m.paths[p].path
+	m.paths[hash].usageCount++
+	return m.paths[hash].path
 }
 
 // RemovePath notifies us that there is one user less for path p
@@ -58,8 +60,10 @@ func (m *BGPPathManager) RemovePath(p BGPPath) {
 		return
 	}
 
-	m.paths[p].usageCount--
-	if m.paths[p].usageCount == 0 {
-		delete(m.paths, p)
+	hash := p.ComputeHash()
+
+	m.paths[hash].usageCount--
+	if m.paths[hash].usageCount == 0 {
+		delete(m.paths, hash)
 	}
 }
diff --git a/route/bgp_path_test.go b/route/bgp_path_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..d0f0a9937a7ded1309664ffd31321d5edf844344
--- /dev/null
+++ b/route/bgp_path_test.go
@@ -0,0 +1,66 @@
+package route
+
+import (
+	"testing"
+
+	"github.com/bio-routing/bio-rd/protocols/bgp/packet"
+	"github.com/stretchr/testify/assert"
+)
+
+func TestCommunitiesString(t *testing.T) {
+	tests := []struct {
+		name     string
+		comms    []uint32
+		expected string
+	}{
+		{
+			name:     "two attributes",
+			comms:    []uint32{131080, 16778241},
+			expected: "(2,8) (256,1025)",
+		},
+	}
+
+	for _, test := range tests {
+		t.Run(test.name, func(te *testing.T) {
+			p := &BGPPath{
+				Communities: test.comms,
+			}
+
+			assert.Equal(te, test.expected, p.CommunitiesString())
+		})
+	}
+}
+
+func TestLargeCommunitiesString(t *testing.T) {
+	tests := []struct {
+		name     string
+		comms    []packet.LargeCommunity
+		expected string
+	}{
+		{
+			name: "two attributes",
+			comms: []packet.LargeCommunity{
+				{
+					GlobalAdministrator: 1,
+					DataPart1:           2,
+					DataPart2:           3,
+				},
+				{
+					GlobalAdministrator: 4,
+					DataPart1:           5,
+					DataPart2:           6,
+				},
+			},
+			expected: "(1,2,3) (4,5,6)",
+		},
+	}
+
+	for _, test := range tests {
+		t.Run(test.name, func(te *testing.T) {
+			p := &BGPPath{
+				LargeCommunities: test.comms,
+			}
+			assert.Equal(te, test.expected, p.LargeCommunitiesString())
+		})
+	}
+}
diff --git a/route/bgp_test.go b/route/bgp_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..2eb017c2a6b33e5174b94cb9d43e4325750e968d
--- /dev/null
+++ b/route/bgp_test.go
@@ -0,0 +1,44 @@
+package route
+
+import (
+	"testing"
+
+	"github.com/bio-routing/bio-rd/protocols/bgp/packet"
+
+	"github.com/stretchr/testify/assert"
+)
+
+func TestComputeHash(t *testing.T) {
+	p := &BGPPath{
+		ASPath: packet.ASPath{
+			packet.ASPathSegment{
+				ASNs:  []uint32{123, 456},
+				Count: 2,
+				Type:  packet.ASSequence,
+			},
+		},
+		BGPIdentifier: 1,
+		Communities: []uint32{
+			123, 456,
+		},
+		EBGP: false,
+		LargeCommunities: []packet.LargeCommunity{
+			packet.LargeCommunity{
+				DataPart1:           1,
+				DataPart2:           2,
+				GlobalAdministrator: 3,
+			},
+		},
+		LocalPref:      100,
+		MED:            1,
+		NextHop:        100,
+		PathIdentifier: 5,
+		Source:         4,
+	}
+
+	assert.Equal(t, "45e238420552b88043edb8cb402034466b08d53b49f8e0fedc680747014ddeff", p.ComputeHash())
+
+	p.LocalPref = 150
+
+	assert.NotEqual(t, "45e238420552b88043edb8cb402034466b08d53b49f8e0fedc680747014ddeff", p.ComputeHash())
+}
diff --git a/routingtable/BUILD.bazel b/routingtable/BUILD.bazel
index 7715828736aa4c22a3ddbdf69dc27646464344d9..1344741ba00193ae2cf24e4e93065d83c62985b0 100644
--- a/routingtable/BUILD.bazel
+++ b/routingtable/BUILD.bazel
@@ -19,7 +19,6 @@ go_library(
         "//net:go_default_library",
         "//protocols/bgp/packet:go_default_library",
         "//route:go_default_library",
-        "//vendor/github.com/sirupsen/logrus:go_default_library",
     ],
 )
 
@@ -35,6 +34,7 @@ go_test(
     embed = [":go_default_library"],
     deps = [
         "//net:go_default_library",
+        "//protocols/bgp/packet:go_default_library",
         "//route:go_default_library",
         "//vendor/github.com/stretchr/testify/assert:go_default_library",
     ],
diff --git a/routingtable/adjRIBIn/BUILD.bazel b/routingtable/adjRIBIn/BUILD.bazel
index 3796a7a878f2fcc9cc8cfe15d39cd1bd38fdf144..6216861c145d1b50f23032fd42102271864449d7 100644
--- a/routingtable/adjRIBIn/BUILD.bazel
+++ b/routingtable/adjRIBIn/BUILD.bazel
@@ -7,7 +7,6 @@ go_library(
     visibility = ["//visibility:public"],
     deps = [
         "//net:go_default_library",
-        "//protocols/bgp/packet:go_default_library",
         "//route:go_default_library",
         "//routingtable:go_default_library",
         "//routingtable/filter:go_default_library",
diff --git a/routingtable/adjRIBIn/adj_rib_in.go b/routingtable/adjRIBIn/adj_rib_in.go
index 2d8500240de4dcc34b571996ea5b50514c414c4a..2cf454be11c610a9c2d2f26a5621382d16d0c388 100644
--- a/routingtable/adjRIBIn/adj_rib_in.go
+++ b/routingtable/adjRIBIn/adj_rib_in.go
@@ -3,7 +3,6 @@ package adjRIBIn
 import (
 	"sync"
 
-	"github.com/bio-routing/bio-rd/protocols/bgp/packet"
 	"github.com/bio-routing/bio-rd/routingtable/filter"
 
 	"github.com/bio-routing/bio-rd/net"
@@ -80,10 +79,7 @@ func (a *AdjRIBIn) AddPath(pfx net.Prefix, p *route.Path) error {
 }
 
 func (a *AdjRIBIn) ourASNsInPath(p *route.Path) bool {
-	// Don't accept path via iBGP which contain our ASN
-	ASPathAttr, _ := packet.ParseASPathStr(p.BGPPath.ASPath)
-
-	for _, pathSegment := range ASPathAttr.Value.(packet.ASPath) {
+	for _, pathSegment := range p.BGPPath.ASPath {
 		for _, asn := range pathSegment.ASNs {
 			if a.contributingASNs.IsContributingASN(asn) {
 				return true
diff --git a/routingtable/adjRIBOut/adj_rib_out.go b/routingtable/adjRIBOut/adj_rib_out.go
index 19b44b8bd2595cc550a960ace7c9c00f1fdab21a..088756dc036c00ebcaefd3c134842fa075af9952 100644
--- a/routingtable/adjRIBOut/adj_rib_out.go
+++ b/routingtable/adjRIBOut/adj_rib_out.go
@@ -52,7 +52,7 @@ func (a *AdjRIBOut) AddPath(pfx bnet.Prefix, p *route.Path) error {
 
 	p = p.Copy()
 	if !a.neighbor.IBGP && !a.neighbor.RouteServerClient {
-		p.BGPPath.ASPath = fmt.Sprintf("%d %s", a.neighbor.LocalASN, p.BGPPath.ASPath)
+		p.BGPPath.Prepend(a.neighbor.LocalASN, 1)
 	}
 
 	if !a.neighbor.IBGP && !a.neighbor.RouteServerClient {
diff --git a/routingtable/adjRIBOut/path_id_manager.go b/routingtable/adjRIBOut/path_id_manager.go
index b13c66745c8d0e9f13bb3944aaa01d4560296780..78054e3ec0294a7ba472e6de3928e2705c5ed536 100644
--- a/routingtable/adjRIBOut/path_id_manager.go
+++ b/routingtable/adjRIBOut/path_id_manager.go
@@ -11,7 +11,7 @@ var maxUint32 = ^uint32(0)
 // pathIDManager manages BGP path identifiers for add-path. This is no thread safe (and doesn't need to be).
 type pathIDManager struct {
 	ids      map[uint32]uint64
-	idByPath map[route.BGPPath]uint32
+	idByPath map[string]uint32
 	last     uint32
 	used     uint32
 }
@@ -19,13 +19,15 @@ type pathIDManager struct {
 func newPathIDManager() *pathIDManager {
 	return &pathIDManager{
 		ids:      make(map[uint32]uint64),
-		idByPath: make(map[route.BGPPath]uint32),
+		idByPath: make(map[string]uint32),
 	}
 }
 
 func (fm *pathIDManager) addPath(p *route.Path) (uint32, error) {
-	if _, exists := fm.idByPath[*p.BGPPath]; exists {
-		id := fm.idByPath[*p.BGPPath]
+	hash := p.BGPPath.ComputeHash()
+
+	if _, exists := fm.idByPath[hash]; exists {
+		id := fm.idByPath[hash]
 		fm.ids[id]++
 		return id, nil
 	}
@@ -43,7 +45,7 @@ func (fm *pathIDManager) addPath(p *route.Path) (uint32, error) {
 		break
 	}
 
-	fm.idByPath[*p.BGPPath] = fm.last
+	fm.idByPath[hash] = fm.last
 	fm.ids[fm.last] = 1
 	fm.used++
 
@@ -51,15 +53,17 @@ func (fm *pathIDManager) addPath(p *route.Path) (uint32, error) {
 }
 
 func (fm *pathIDManager) releasePath(p *route.Path) (uint32, error) {
-	if _, exists := fm.idByPath[*p.BGPPath]; !exists {
+	hash := p.BGPPath.ComputeHash()
+
+	if _, exists := fm.idByPath[hash]; !exists {
 		return 0, fmt.Errorf("ID not found for path: %s", p.Print())
 	}
 
-	id := fm.idByPath[*p.BGPPath]
+	id := fm.idByPath[hash]
 	fm.ids[id]--
 	if fm.ids[id] == 0 {
-		delete(fm.ids, fm.idByPath[*p.BGPPath])
-		delete(fm.idByPath, *p.BGPPath)
+		delete(fm.ids, fm.idByPath[hash])
+		delete(fm.idByPath, hash)
 	}
 	fm.used--
 
diff --git a/routingtable/filter/actions/add_community_action.go b/routingtable/filter/actions/add_community_action.go
index 01ad05f62936574bb576d55fe1c7bcf23697a38f..c24107e8b94be6657174fbd3dffb06ae978ff767 100644
--- a/routingtable/filter/actions/add_community_action.go
+++ b/routingtable/filter/actions/add_community_action.go
@@ -1,10 +1,7 @@
 package actions
 
 import (
-	"strings"
-
 	"github.com/bio-routing/bio-rd/net"
-	"github.com/bio-routing/bio-rd/protocols/bgp/packet"
 	"github.com/bio-routing/bio-rd/route"
 )
 
@@ -26,9 +23,8 @@ func (a *AddCommunityAction) Do(p net.Prefix, pa *route.Path) (modPath *route.Pa
 	modified := pa.Copy()
 
 	for _, com := range a.communities {
-		modified.BGPPath.Communities = modified.BGPPath.Communities + " " + packet.CommunityStringForUint32(com)
+		modified.BGPPath.Communities = append(modified.BGPPath.Communities, com)
 	}
-	modified.BGPPath.Communities = strings.TrimLeft(modified.BGPPath.Communities, " ")
 
 	return modified, false
 }
diff --git a/routingtable/filter/actions/add_community_action_test.go b/routingtable/filter/actions/add_community_action_test.go
index 0687e86c4bc80b8648d172cb125449962dea9cf6..9ec3d1401b87da808c324a984e1d5ef7620658cd 100644
--- a/routingtable/filter/actions/add_community_action_test.go
+++ b/routingtable/filter/actions/add_community_action_test.go
@@ -11,7 +11,7 @@ import (
 func TestAddingCommunities(t *testing.T) {
 	tests := []struct {
 		name        string
-		current     string
+		current     []uint32
 		communities []uint32
 		expected    string
 	}{
@@ -23,16 +23,20 @@ func TestAddingCommunities(t *testing.T) {
 			expected: "(1,2)",
 		},
 		{
-			name:    "add one to existing",
-			current: "(1,2)",
+			name: "add one to existing",
+			current: []uint32{
+				65538,
+			},
 			communities: []uint32{
 				196612,
 			},
 			expected: "(1,2) (3,4)",
 		},
 		{
-			name:    "add two to existing",
-			current: "(1,2)",
+			name: "add two to existing",
+			current: []uint32{
+				65538,
+			},
 			communities: []uint32{
 				196612, 327686,
 			},
@@ -41,7 +45,7 @@ func TestAddingCommunities(t *testing.T) {
 	}
 
 	for _, test := range tests {
-		t.Run(test.name, func(te *testing.T) {
+		t.Run(test.name, func(t *testing.T) {
 			p := &route.Path{
 				BGPPath: &route.BGPPath{
 					Communities: test.current,
@@ -51,7 +55,7 @@ func TestAddingCommunities(t *testing.T) {
 			a := NewAddCommunityAction(test.communities)
 			modPath, _ := a.Do(net.Prefix{}, p)
 
-			assert.Equal(te, test.expected, modPath.BGPPath.Communities)
+			assert.Equal(t, test.expected, modPath.BGPPath.CommunitiesString())
 		})
 	}
 }
diff --git a/routingtable/filter/actions/add_large_community_action.go b/routingtable/filter/actions/add_large_community_action.go
index 17169c315817509c8c6c1a2b382ab39c3b5f0428..534ad30b66e4580ca6b8078bf7a077f87b6e758f 100644
--- a/routingtable/filter/actions/add_large_community_action.go
+++ b/routingtable/filter/actions/add_large_community_action.go
@@ -1,18 +1,16 @@
 package actions
 
 import (
-	"strings"
-
 	"github.com/bio-routing/bio-rd/net"
 	"github.com/bio-routing/bio-rd/protocols/bgp/packet"
 	"github.com/bio-routing/bio-rd/route"
 )
 
 type AddLargeCommunityAction struct {
-	communities []*packet.LargeCommunity
+	communities []packet.LargeCommunity
 }
 
-func NewAddLargeCommunityAction(coms []*packet.LargeCommunity) *AddLargeCommunityAction {
+func NewAddLargeCommunityAction(coms []packet.LargeCommunity) *AddLargeCommunityAction {
 	return &AddLargeCommunityAction{
 		communities: coms,
 	}
@@ -24,11 +22,7 @@ func (a *AddLargeCommunityAction) Do(p net.Prefix, pa *route.Path) (modPath *rou
 	}
 
 	modified := pa.Copy()
-
-	for _, com := range a.communities {
-		modified.BGPPath.LargeCommunities = modified.BGPPath.LargeCommunities + " " + com.String()
-	}
-	modified.BGPPath.LargeCommunities = strings.TrimLeft(modified.BGPPath.LargeCommunities, " ")
+	modified.BGPPath.LargeCommunities = append(modified.BGPPath.LargeCommunities, a.communities...)
 
 	return modified, false
 }
diff --git a/routingtable/filter/actions/add_large_community_action_test.go b/routingtable/filter/actions/add_large_community_action_test.go
index d2c597fd5cbe8011b3eeb7b74443a09c726d6eb7..f7af91396fdd7d697cf667d7712e18c9784d68e3 100644
--- a/routingtable/filter/actions/add_large_community_action_test.go
+++ b/routingtable/filter/actions/add_large_community_action_test.go
@@ -12,14 +12,14 @@ import (
 func TestAddingLargeCommunities(t *testing.T) {
 	tests := []struct {
 		name        string
-		current     string
-		communities []*packet.LargeCommunity
+		current     []packet.LargeCommunity
+		communities []packet.LargeCommunity
 		expected    string
 	}{
 		{
 			name: "add one to empty",
-			communities: []*packet.LargeCommunity{
-				&packet.LargeCommunity{
+			communities: []packet.LargeCommunity{
+				packet.LargeCommunity{
 					GlobalAdministrator: 1,
 					DataPart1:           2,
 					DataPart2:           3,
@@ -28,10 +28,16 @@ func TestAddingLargeCommunities(t *testing.T) {
 			expected: "(1,2,3)",
 		},
 		{
-			name:    "add one to existing",
-			current: "(5,6,7)",
-			communities: []*packet.LargeCommunity{
-				&packet.LargeCommunity{
+			name: "add one to existing",
+			current: []packet.LargeCommunity{
+				packet.LargeCommunity{
+					GlobalAdministrator: 5,
+					DataPart1:           6,
+					DataPart2:           7,
+				},
+			},
+			communities: []packet.LargeCommunity{
+				packet.LargeCommunity{
 					GlobalAdministrator: 1,
 					DataPart1:           2,
 					DataPart2:           3,
@@ -40,15 +46,21 @@ func TestAddingLargeCommunities(t *testing.T) {
 			expected: "(5,6,7) (1,2,3)",
 		},
 		{
-			name:    "add two to existing",
-			current: "(5,6,7)",
-			communities: []*packet.LargeCommunity{
-				&packet.LargeCommunity{
+			name: "add two to existing",
+			current: []packet.LargeCommunity{
+				packet.LargeCommunity{
+					GlobalAdministrator: 5,
+					DataPart1:           6,
+					DataPart2:           7,
+				},
+			},
+			communities: []packet.LargeCommunity{
+				packet.LargeCommunity{
 					GlobalAdministrator: 1,
 					DataPart1:           2,
 					DataPart2:           3,
 				},
-				&packet.LargeCommunity{
+				packet.LargeCommunity{
 					GlobalAdministrator: 7,
 					DataPart1:           8,
 					DataPart2:           9,
@@ -69,7 +81,7 @@ func TestAddingLargeCommunities(t *testing.T) {
 			a := NewAddLargeCommunityAction(test.communities)
 			modPath, _ := a.Do(net.Prefix{}, p)
 
-			assert.Equal(te, test.expected, modPath.BGPPath.LargeCommunities)
+			assert.Equal(te, test.expected, modPath.BGPPath.LargeCommunitiesString())
 		})
 	}
 }
diff --git a/routingtable/filter/actions/as_path_prepend_action_test.go b/routingtable/filter/actions/as_path_prepend_action_test.go
index 348116ccb33b7e067e7e50effa438b9eb0d42c3a..253c119ffc91d702e0f4d4cf31e1f88c35d2008c 100644
--- a/routingtable/filter/actions/as_path_prepend_action_test.go
+++ b/routingtable/filter/actions/as_path_prepend_action_test.go
@@ -3,6 +3,8 @@ package actions
 import (
 	"testing"
 
+	"github.com/bio-routing/bio-rd/protocols/bgp/packet"
+
 	"github.com/bio-routing/bio-rd/net"
 	"github.com/bio-routing/bio-rd/route"
 	"github.com/stretchr/testify/assert"
@@ -23,7 +25,13 @@ func TestAppendPath(t *testing.T) {
 			name:  "append 0",
 			times: 0,
 			bgpPath: &route.BGPPath{
-				ASPath:    "12345 12345",
+				ASPath: packet.ASPath{
+					packet.ASPathSegment{
+						Count: 2,
+						Type:  packet.ASSequence,
+						ASNs:  []uint32{12345, 12345},
+					},
+				},
 				ASPathLen: 2,
 			},
 			expectedPath:   "12345 12345",
@@ -33,7 +41,13 @@ func TestAppendPath(t *testing.T) {
 			name:  "append 3",
 			times: 3,
 			bgpPath: &route.BGPPath{
-				ASPath:    "12345 15169",
+				ASPath: packet.ASPath{
+					packet.ASPathSegment{
+						Count: 2,
+						Type:  packet.ASSequence,
+						ASNs:  []uint32{12345, 15169},
+					},
+				},
 				ASPathLen: 2,
 			},
 			expectedPath:   "12345 12345 12345 12345 15169",
@@ -52,7 +66,7 @@ func TestAppendPath(t *testing.T) {
 				return
 			}
 
-			assert.Equal(te, test.expectedPath, p.BGPPath.ASPath, "ASPath")
+			assert.Equal(te, test.expectedPath, p.BGPPath.ASPath.String(), "ASPath")
 			assert.Equal(te, test.expectedLength, p.BGPPath.ASPathLen, "ASPathLen")
 		})
 	}
diff --git a/routingtable/filter/community_filter.go b/routingtable/filter/community_filter.go
index 9d384e54ad0a58ee622e3d2b0bb1b857c867b0c3..b07d4b332349de507ca5591010475ebd920fed94 100644
--- a/routingtable/filter/community_filter.go
+++ b/routingtable/filter/community_filter.go
@@ -1,15 +1,15 @@
 package filter
 
-import (
-	"strings"
-
-	"github.com/bio-routing/bio-rd/protocols/bgp/packet"
-)
-
 type CommunityFilter struct {
 	community uint32
 }
 
-func (f *CommunityFilter) Matches(communityString string) bool {
-	return strings.Contains(communityString, packet.CommunityStringForUint32(f.community))
+func (f *CommunityFilter) Matches(coms []uint32) bool {
+	for _, com := range coms {
+		if com == f.community {
+			return true
+		}
+	}
+
+	return false
 }
diff --git a/routingtable/filter/large_community_filter.go b/routingtable/filter/large_community_filter.go
index 0f9d474e69728ba682cde441e819a0d08bd7cc77..cb4722bdfe98a68cd6c6e9938f188bbbf606c968 100644
--- a/routingtable/filter/large_community_filter.go
+++ b/routingtable/filter/large_community_filter.go
@@ -1,15 +1,19 @@
 package filter
 
 import (
-	"strings"
-
 	"github.com/bio-routing/bio-rd/protocols/bgp/packet"
 )
 
 type LargeCommunityFilter struct {
-	community *packet.LargeCommunity
+	community packet.LargeCommunity
 }
 
-func (f *LargeCommunityFilter) Matches(communityString string) bool {
-	return strings.Contains(communityString, f.community.String())
+func (f *LargeCommunityFilter) Matches(coms []packet.LargeCommunity) bool {
+	for _, com := range coms {
+		if com == f.community {
+			return true
+		}
+	}
+
+	return false
 }
diff --git a/routingtable/filter/term_condition_test.go b/routingtable/filter/term_condition_test.go
index cca90950aa5a443c042c8061f77a08d3f4f7a0c4..e1b3d4bd6173b196ddaca029e768274a08364883 100644
--- a/routingtable/filter/term_condition_test.go
+++ b/routingtable/filter/term_condition_test.go
@@ -107,7 +107,7 @@ func TestMatches(t *testing.T) {
 			name:   "community matches",
 			prefix: net.NewPfx(strAddr("10.0.0.0"), 24),
 			bgpPath: &route.BGPPath{
-				Communities: "(1,2) (3,4) (5,6)",
+				Communities: []uint32{65538, 196612, 327686}, // (1,2) (3,4) (5,6)
 			},
 			communityFilters: []*CommunityFilter{
 				&CommunityFilter{196612}, // (3,4)
@@ -118,7 +118,7 @@ func TestMatches(t *testing.T) {
 			name:   "community does not match",
 			prefix: net.NewPfx(strAddr("10.0.0.0"), 24),
 			bgpPath: &route.BGPPath{
-				Communities: "(1,2) (3,4) (5,6)",
+				Communities: []uint32{65538, 196612, 327686}, // (1,2) (3,4) (5,6)
 			},
 			communityFilters: []*CommunityFilter{
 				&CommunityFilter{196608}, // (3,0)
@@ -137,11 +137,22 @@ func TestMatches(t *testing.T) {
 			name:   "large community matches",
 			prefix: net.NewPfx(strAddr("10.0.0.0"), 24),
 			bgpPath: &route.BGPPath{
-				LargeCommunities: "(1,2,0) (1,2,3)",
+				LargeCommunities: []packet.LargeCommunity{
+					packet.LargeCommunity{
+						GlobalAdministrator: 1,
+						DataPart1:           2,
+						DataPart2:           3,
+					},
+					packet.LargeCommunity{
+						GlobalAdministrator: 1,
+						DataPart1:           2,
+						DataPart2:           0,
+					},
+				},
 			},
 			largeCommunityFilters: []*LargeCommunityFilter{
 				{
-					&packet.LargeCommunity{
+					packet.LargeCommunity{
 						GlobalAdministrator: 1,
 						DataPart1:           2,
 						DataPart2:           3,
@@ -156,7 +167,7 @@ func TestMatches(t *testing.T) {
 			bgpPath: &route.BGPPath{},
 			largeCommunityFilters: []*LargeCommunityFilter{
 				{
-					&packet.LargeCommunity{
+					packet.LargeCommunity{
 						GlobalAdministrator: 1,
 						DataPart1:           2,
 						DataPart2:           3,
@@ -170,7 +181,7 @@ func TestMatches(t *testing.T) {
 			prefix: net.NewPfx(strAddr("10.0.0.0"), 24),
 			largeCommunityFilters: []*LargeCommunityFilter{
 				{
-					&packet.LargeCommunity{
+					packet.LargeCommunity{
 						GlobalAdministrator: 1,
 						DataPart1:           2,
 						DataPart2:           3,
diff --git a/routingtable/update_helper.go b/routingtable/update_helper.go
index c18936f3a21055678401a9ec4697ab00363fc4f0..6709bd43ab7e2d39914c6ebd1c59abb6518e8189 100644
--- a/routingtable/update_helper.go
+++ b/routingtable/update_helper.go
@@ -1,12 +1,9 @@
 package routingtable
 
 import (
-	"strings"
-
 	"github.com/bio-routing/bio-rd/net"
 	"github.com/bio-routing/bio-rd/protocols/bgp/packet"
 	"github.com/bio-routing/bio-rd/route"
-	log "github.com/sirupsen/logrus"
 )
 
 // ShouldPropagateUpdate performs some default checks and returns if an route update should be propagated to a neighbor
@@ -32,17 +29,7 @@ func isDisallowedByCommunity(p *route.Path, n *Neighbor) bool {
 		return false
 	}
 
-	strs := strings.Split(p.BGPPath.Communities, " ")
-	for _, str := range strs {
-		com, err := packet.ParseCommunityString(str)
-		if err != nil {
-			log.WithField("Sender", "routingtable.ShouldAnnounce()").
-				WithField("community", str).
-				WithError(err).
-				Error("Could not parse community")
-			continue
-		}
-
+	for _, com := range p.BGPPath.Communities {
 		if (com == packet.WellKnownCommunityNoExport && !n.IBGP) || com == packet.WellKnownCommunityNoAdvertise {
 			return true
 		}
diff --git a/routingtable/update_helper_test.go b/routingtable/update_helper_test.go
index 29342f376ead000006039e6aa95caf3399937dc1..452a7e4af04152e21c13631c38190b8d4795649d 100644
--- a/routingtable/update_helper_test.go
+++ b/routingtable/update_helper_test.go
@@ -2,9 +2,11 @@ package routingtable
 
 import (
 	"net"
+	"strings"
 	"testing"
 
 	bnet "github.com/bio-routing/bio-rd/net"
+	"github.com/bio-routing/bio-rd/protocols/bgp/packet"
 	"github.com/bio-routing/bio-rd/route"
 	"github.com/stretchr/testify/assert"
 )
@@ -58,19 +60,31 @@ func TestShouldPropagateUpdate(t *testing.T) {
 	}
 
 	for _, test := range tests {
-		t.Run(test.name, func(te *testing.T) {
-			pfx := bnet.NewPfx(0, 32)
+		t.Run(test.name, func(t *testing.T) {
+			comms := make([]uint32, 0)
+			for _, s := range strings.Split(test.communities, " ") {
+				if s == "" {
+					continue
+				}
+
+				com, err := packet.ParseCommunityString(s)
+				if err != nil {
+					t.Fatalf("test failed: %s", err)
+				}
+				comms = append(comms, com)
+			}
 
+			pfx := bnet.NewPfx(0, 32)
 			pa := &route.Path{
 				Type: route.BGPPathType,
 				BGPPath: &route.BGPPath{
-					Communities: test.communities,
+					Communities: comms,
 					Source:      bnet.IPv4ToUint32(net.ParseIP("192.168.1.1")),
 				},
 			}
 
 			res := ShouldPropagateUpdate(pfx, pa, &test.neighbor)
-			assert.Equal(te, test.expected, res)
+			assert.Equal(t, test.expected, res)
 		})
 	}
 }