diff --git a/main.go b/main.go
index 3e0598bce10567ead6b30ea7f9e1caeebeafdcf3..1952d771d9713831e851f7c7388b532ba1f4f7f1 100644
--- a/main.go
+++ b/main.go
@@ -22,6 +22,10 @@ func main() {
 
 	err := b.Start(&config.Global{
 		Listen: true,
+		LocalAddressList: []net.IP{
+			net.IPv4(169, 254, 100, 1),
+			net.IPv4(169, 254, 200, 0),
+		},
 	})
 	if err != nil {
 		logrus.Fatalf("Unable to start BGP server: %v", err)
diff --git a/protocols/bgp/packet/bgp.go b/protocols/bgp/packet/bgp.go
index 7cd77c21d0a6e1ce87e8f3e3926433b336a8cd25..b32980dbdb3f702a69af0ff521db0cb66584bd4b 100644
--- a/protocols/bgp/packet/bgp.go
+++ b/protocols/bgp/packet/bgp.go
@@ -6,11 +6,12 @@ const (
 	BGP4Version    = 4
 	MinOpenLen     = 29
 
-	MarkerLen  = 16
-	HeaderLen  = 19
-	MinLen     = 19
-	MaxLen     = 4096
-	NLRIMaxLen = 5
+	MarkerLen         = 16
+	HeaderLen         = 19
+	MinLen            = 19
+	MaxLen            = 4096
+	NLRIMaxLen        = 5
+	LargeCommunityLen = 12
 
 	OpenMsg         = 1
 	UpdateMsg       = 2
@@ -55,17 +56,17 @@ const (
 	AdministrativeReset    = 4
 
 	// Attribute Type Codes
-	OriginAttr      = 1
-	ASPathAttr      = 2
-	NextHopAttr     = 3
-	MEDAttr         = 4
-	LocalPrefAttr   = 5
-	AtomicAggrAttr  = 6
-	AggregatorAttr  = 7
-	CommunitiesAttr = 8
-
-	AS4PathAttr       = 17
-	AS4AggregatorAttr = 18
+	OriginAttr         = 1
+	ASPathAttr         = 2
+	NextHopAttr        = 3
+	MEDAttr            = 4
+	LocalPrefAttr      = 5
+	AtomicAggrAttr     = 6
+	AggregatorAttr     = 7
+	CommunitiesAttr    = 8
+	AS4PathAttr        = 17
+	AS4AggregatorAttr  = 18
+	LargeCommunityAttr = 32
 
 	// ORIGIN values
 	IGP        = 0
diff --git a/protocols/bgp/packet/large_community.go b/protocols/bgp/packet/large_community.go
new file mode 100644
index 0000000000000000000000000000000000000000..135c86398700e200ba3a4f42e75ca3df33f30ed6
--- /dev/null
+++ b/protocols/bgp/packet/large_community.go
@@ -0,0 +1,46 @@
+package packet
+
+import (
+	"fmt"
+	"strconv"
+	"strings"
+)
+
+type LargeCommunity struct {
+	GlobalAdministrator uint32
+	DataPart1           uint32
+	DataPart2           uint32
+}
+
+func (c LargeCommunity) String() string {
+	return fmt.Sprintf("(%d,%d,%d)", c.GlobalAdministrator, c.DataPart1, c.DataPart2)
+}
+
+func ParseCommunityString(s string) (com LargeCommunity, err error) {
+	s = strings.Trim(s, "()")
+	t := strings.Split(s, ",")
+
+	if len(t) != 3 {
+		return com, fmt.Errorf("can not parse large community %s", s)
+	}
+
+	v, err := strconv.ParseUint(t[0], 10, 32)
+	if err != nil {
+		return com, err
+	}
+	com.GlobalAdministrator = uint32(v)
+
+	v, err = strconv.ParseUint(t[1], 10, 32)
+	if err != nil {
+		return com, err
+	}
+	com.DataPart1 = uint32(v)
+
+	v, err = strconv.ParseUint(t[2], 10, 32)
+	if err != nil {
+		return com, err
+	}
+	com.DataPart2 = uint32(v)
+
+	return com, err
+}
diff --git a/protocols/bgp/packet/path_attributes.go b/protocols/bgp/packet/path_attributes.go
index 753395d2c67a844863e067f27ec45bed1a29cda2..a9fcdd74e5eaab291cce876a8ad597c03ca0c2e4 100644
--- a/protocols/bgp/packet/path_attributes.go
+++ b/protocols/bgp/packet/path_attributes.go
@@ -96,6 +96,10 @@ func decodePathAttr(buf *bytes.Buffer) (pa *PathAttribute, consumed uint16, err
 		if err := pa.decodeAS4Aggregator(buf); err != nil {
 			return nil, consumed, fmt.Errorf("Failed to skip not supported AS4Aggregator: %v", err)
 		}
+	case LargeCommunityAttr:
+		if err := pa.decodeLargeCommunities(buf); err != nil {
+			return nil, consumed, fmt.Errorf("Failed to decode large communities: %v", err)
+		}
 	default:
 		if err := pa.decodeUnknown(buf); err != nil {
 			return nil, consumed, fmt.Errorf("Failed to decode unknown attribute: %v", err)
@@ -176,41 +180,15 @@ func (pa *PathAttribute) decodeASPath(buf *bytes.Buffer) error {
 }
 
 func (pa *PathAttribute) decodeNextHop(buf *bytes.Buffer) error {
-	addr := [4]byte{}
-
-	p := uint16(0)
-	n, err := buf.Read(addr[:])
-	if err != nil {
-		return err
-	}
-	if n != 4 {
-		return fmt.Errorf("Unable to read next hop: buf.Read read %d bytes", n)
-	}
-
-	pa.Value = fourBytesToUint32(addr)
-	p += 4
-
-	return dumpNBytes(buf, pa.Length-p)
+	return pa.decodeUint32(buf, "next hop")
 }
 
 func (pa *PathAttribute) decodeMED(buf *bytes.Buffer) error {
-	med, err := pa.decodeUint32(buf)
-	if err != nil {
-		return fmt.Errorf("Unable to recode local pref: %v", err)
-	}
-
-	pa.Value = uint32(med)
-	return nil
+	return pa.decodeUint32(buf, "MED")
 }
 
 func (pa *PathAttribute) decodeLocalPref(buf *bytes.Buffer) error {
-	lpref, err := pa.decodeUint32(buf)
-	if err != nil {
-		return fmt.Errorf("Unable to recode local pref: %v", err)
-	}
-
-	pa.Value = uint32(lpref)
-	return nil
+	return pa.decodeUint32(buf, "local pref")
 }
 
 func (pa *PathAttribute) decodeAggregator(buf *bytes.Buffer) error {
@@ -260,23 +238,64 @@ func (pa *PathAttribute) decodeCommunities(buf *bytes.Buffer) error {
 	return nil
 }
 
-func (pa *PathAttribute) decodeAS4Path(buf *bytes.Buffer) error {
-	as4Path, err := pa.decodeUint32(buf)
-	if err != nil {
-		return fmt.Errorf("Unable to decode AS4Path: %v", err)
+func (pa *PathAttribute) decodeLargeCommunities(buf *bytes.Buffer) error {
+	length := pa.Length
+	count := length / LargeCommunityLen
+
+	coms := make([]LargeCommunity, count)
+
+	for i := uint16(0); i < count; i++ {
+		com := LargeCommunity{}
+
+		v, err := read4BytesAsUint32(buf)
+		if err != nil {
+			return err
+		}
+		com.GlobalAdministrator = v
+
+		v, err = read4BytesAsUint32(buf)
+		if err != nil {
+			return err
+		}
+		com.DataPart1 = v
+
+		v, err = read4BytesAsUint32(buf)
+		if err != nil {
+			return err
+		}
+		com.DataPart2 = v
+
+		coms[i] = com
 	}
 
-	pa.Value = as4Path
-	return nil
+	pa.Value = coms
+
+	dump := pa.Length - (count * LargeCommunityLen)
+	return dumpNBytes(buf, dump)
+}
+
+func (pa *PathAttribute) decodeAS4Path(buf *bytes.Buffer) error {
+	return pa.decodeUint32(buf, "AS4Path")
 }
 
 func (pa *PathAttribute) decodeAS4Aggregator(buf *bytes.Buffer) error {
-	as4Aggregator, err := pa.decodeUint32(buf)
+	return pa.decodeUint32(buf, "AS4Aggregator")
+}
+
+func (pa *PathAttribute) decodeUint32(buf *bytes.Buffer, attrName string) error {
+	v, err := read4BytesAsUint32(buf)
 	if err != nil {
-		return fmt.Errorf("Unable to decode AS4Aggregator: %v", err)
+		return fmt.Errorf("Unable to decode %s: %v", attrName, err)
+	}
+
+	pa.Value = v
+
+	p := uint16(4)
+	err = dumpNBytes(buf, pa.Length-p)
+	if err != nil {
+		return fmt.Errorf("dumpNBytes failed: %v", err)
 	}
 
-	pa.Value = as4Aggregator
 	return nil
 }
 
@@ -300,24 +319,6 @@ func (pa *PathAttribute) setLength(buf *bytes.Buffer) (int, error) {
 	return bytesRead, nil
 }
 
-func (pa *PathAttribute) decodeUint32(buf *bytes.Buffer) (uint32, error) {
-	var v uint32
-
-	p := uint16(0)
-	err := decode(buf, []interface{}{&v})
-	if err != nil {
-		return 0, err
-	}
-
-	p += 4
-	err = dumpNBytes(buf, pa.Length-p)
-	if err != nil {
-		return 0, fmt.Errorf("dumpNBytes failed: %v", err)
-	}
-
-	return v, nil
-}
-
 func (pa *PathAttribute) ASPathString() (ret string) {
 	for _, p := range pa.Value.(ASPath) {
 		if p.Type == ASSet {
@@ -351,6 +352,15 @@ func (pa *PathAttribute) ASPathLen() (ret uint16) {
 	return
 }
 
+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 {
@@ -383,6 +393,8 @@ func (pa *PathAttribute) serialize(buf *bytes.Buffer) uint8 {
 		pathAttrLen = pa.serializeAtomicAggregate(buf)
 	case AggregatorAttr:
 		pathAttrLen = pa.serializeAggregator(buf)
+	case LargeCommunityAttr:
+		pathAttrLen = pa.serializeLargeCommunities(buf)
 	}
 
 	return pathAttrLen
@@ -478,6 +490,32 @@ func (pa *PathAttribute) serializeAggregator(buf *bytes.Buffer) uint8 {
 	return 5
 }
 
+func (pa *PathAttribute) serializeLargeCommunities(buf *bytes.Buffer) uint8 {
+	coms := pa.Value.([]LargeCommunity)
+	if len(coms) == 0 {
+		return 0
+	}
+
+	attrFlags := uint8(0)
+	attrFlags = setOptional(attrFlags)
+	attrFlags = setTransitive(attrFlags)
+	attrFlags = setPartial(attrFlags)
+	buf.WriteByte(attrFlags)
+	buf.WriteByte(LargeCommunityAttr)
+
+	length := uint8(LargeCommunityLen * len(coms))
+
+	buf.WriteByte(length)
+
+	for _, com := range coms {
+		buf.Write(convert.Uint32Byte(com.GlobalAdministrator))
+		buf.Write(convert.Uint32Byte(com.DataPart1))
+		buf.Write(convert.Uint32Byte(com.DataPart2))
+	}
+
+	return length
+}
+
 /*func (pa *PathAttribute) PrependASPath(prepend []uint32) {
 	if pa.TypeCode != ASPathAttr {
 		return
@@ -563,6 +601,24 @@ func ParseASPathStr(asPathString string) (*PathAttribute, error) {
 	}, 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 = ParseCommunityString(str)
+		if err != nil {
+			return nil, err
+		}
+	}
+
+	return &PathAttribute{
+		TypeCode: LargeCommunityAttr,
+		Value:    coms,
+	}, nil
+}
+
 func isBeginOfASSet(asPathPart string) bool {
 	return strings.Contains(asPathPart, "(")
 }
@@ -574,3 +630,16 @@ func isEndOfASSset(asPathPart string) bool {
 func fourBytesToUint32(address [4]byte) uint32 {
 	return uint32(address[0])<<24 + uint32(address[1])<<16 + uint32(address[2])<<8 + uint32(address[3])
 }
+
+func read4BytesAsUint32(buf *bytes.Buffer) (uint32, error) {
+	b := [4]byte{}
+	n, err := buf.Read(b[:])
+	if err != nil {
+		return 0, err
+	}
+	if n != 4 {
+		return 0, fmt.Errorf("Unable to read as uint32. Expected 4 bytes but got only %d", n)
+	}
+
+	return fourBytesToUint32(b), nil
+}
diff --git a/protocols/bgp/packet/path_attributes_test.go b/protocols/bgp/packet/path_attributes_test.go
index a547178a7c2422ba5dc1b653735d590f3b69973b..47e00b35a2ce63580c5f669a20485732875c6231 100644
--- a/protocols/bgp/packet/path_attributes_test.go
+++ b/protocols/bgp/packet/path_attributes_test.go
@@ -589,6 +589,74 @@ func TestDecodeAggregator(t *testing.T) {
 	}
 }
 
+func TestDecodeLargeCommunity(t *testing.T) {
+	tests := []struct {
+		name           string
+		input          []byte
+		wantFail       bool
+		explicitLength uint16
+		expected       *PathAttribute
+	}{
+		{
+			name: "two valid large communities",
+			input: []byte{
+				0, 0, 0, 1, 0, 0, 0, 2, 0, 0, 0, 3, 0, 0, 0, 4, 0, 0, 0, 5, 0, 0, 0, 6, // (1, 2, 3), (4, 5, 6)
+			},
+			wantFail: false,
+			expected: &PathAttribute{
+				Length: 24,
+				Value: []LargeCommunity{
+					{
+						GlobalAdministrator: 1,
+						DataPart1:           2,
+						DataPart2:           3,
+					},
+					{
+						GlobalAdministrator: 4,
+						DataPart1:           5,
+						DataPart2:           6,
+					},
+				},
+			},
+		},
+		{
+			name:     "Empty input",
+			input:    []byte{},
+			wantFail: false,
+			expected: &PathAttribute{
+				Length: 0,
+				Value:  []LargeCommunity{},
+			},
+		},
+	}
+
+	for _, test := range tests {
+		l := uint16(len(test.input))
+		if test.explicitLength != 0 {
+			l = test.explicitLength
+		}
+		pa := &PathAttribute{
+			Length: l,
+		}
+		err := pa.decodeLargeCommunities(bytes.NewBuffer(test.input))
+
+		if test.wantFail {
+			if err != nil {
+				continue
+			}
+			t.Errorf("Expected error did not happen for test %q", test.name)
+			continue
+		}
+
+		if err != nil {
+			t.Errorf("Unexpected failure for test %q: %v", test.name, err)
+			continue
+		}
+
+		assert.Equal(t, test.expected, pa)
+	}
+}
+
 func TestSetLength(t *testing.T) {
 	tests := []struct {
 		name             string
@@ -695,7 +763,7 @@ func TestDecodeUint32(t *testing.T) {
 		pa := &PathAttribute{
 			Length: l,
 		}
-		res, err := pa.decodeUint32(bytes.NewBuffer(test.input))
+		err := pa.decodeUint32(bytes.NewBuffer(test.input), "test")
 
 		if test.wantFail {
 			if err != nil {
@@ -710,7 +778,7 @@ func TestDecodeUint32(t *testing.T) {
 			continue
 		}
 
-		assert.Equal(t, test.expected, res)
+		assert.Equal(t, test.expected, pa.Value)
 	}
 }
 
@@ -756,6 +824,40 @@ func TestASPathString(t *testing.T) {
 	}
 }
 
+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 TestSetOptional(t *testing.T) {
 	tests := []struct {
 		name     string
@@ -1093,6 +1195,62 @@ func TestSerializeASPath(t *testing.T) {
 	}
 }
 
+func TestSerializeLargeCommunities(t *testing.T) {
+	tests := []struct {
+		name        string
+		input       *PathAttribute
+		expected    []byte
+		expectedLen uint8
+	}{
+		{
+			name: "2 large communities",
+			input: &PathAttribute{
+				TypeCode: LargeCommunityAttr,
+				Value: []LargeCommunity{
+					{
+						GlobalAdministrator: 1,
+						DataPart1:           2,
+						DataPart2:           3,
+					},
+					{
+						GlobalAdministrator: 4,
+						DataPart1:           5,
+						DataPart2:           6,
+					},
+				},
+			},
+			expected: []byte{
+				0xe0,                                                                   // Attribute flags
+				32,                                                                     // Type
+				24,                                                                     // Length
+				0, 0, 0, 1, 0, 0, 0, 2, 0, 0, 0, 3, 0, 0, 0, 4, 0, 0, 0, 5, 0, 0, 0, 6, // Communities (1, 2, 3), (4, 5, 6)
+			},
+			expectedLen: 24,
+		},
+		{
+			name: "empty list of communities",
+			input: &PathAttribute{
+				TypeCode: LargeCommunityAttr,
+				Value:    []LargeCommunity{},
+			},
+			expected:    []byte{},
+			expectedLen: 0,
+		},
+	}
+
+	for _, test := range tests {
+		t.Run(test.name, func(te *testing.T) {
+			buf := bytes.NewBuffer([]byte{})
+			n := test.input.serializeLargeCommunities(buf)
+			if n != test.expectedLen {
+				t.Fatalf("Unexpected length for test %q: %d", test.name, n)
+			}
+
+			assert.Equal(t, test.expected, buf.Bytes())
+		})
+	}
+}
+
 func TestSerialize(t *testing.T) {
 	tests := []struct {
 		name     string
diff --git a/protocols/bgp/server/fsm.go b/protocols/bgp/server/fsm.go
index 6e37f14ea9ae53866beb699cc7748e158faf0523..1d83b2d39819e429d9d32b557df9d97d28e07bac 100644
--- a/protocols/bgp/server/fsm.go
+++ b/protocols/bgp/server/fsm.go
@@ -792,7 +792,7 @@ func (fsm *FSM) established() int {
 		case recvMsg := <-fsm.msgRecvCh:
 			msg, err := packet.Decode(bytes.NewBuffer(recvMsg.msg))
 			if err != nil {
-        log.WithError(err).Errorf("Failed to decode BGP message %v\n", recvMsg.msg)
+				log.WithError(err).Errorf("Failed to decode BGP message %v\n", recvMsg.msg)
 				switch bgperr := err.(type) {
 				case packet.BGPError:
 					sendNotification(fsm.con, bgperr.ErrorCode, bgperr.ErrorSubCode)
@@ -845,6 +845,8 @@ func (fsm *FSM) established() int {
 						case packet.ASPathAttr:
 							path.BGPPath.ASPath = pa.ASPathString()
 							path.BGPPath.ASPathLen = pa.ASPathLen()
+						case packet.LargeCommunityAttr:
+							path.BGPPath.LargeCommunities = pa.LargeCommunityString()
 						}
 					}
 					fsm.adjRIBIn.AddPath(pfx, path)
diff --git a/protocols/bgp/server/update_helper.go b/protocols/bgp/server/update_helper.go
new file mode 100644
index 0000000000000000000000000000000000000000..22bfeec52ad72e603583452c9bc9f70be314f4a7
--- /dev/null
+++ b/protocols/bgp/server/update_helper.go
@@ -0,0 +1,60 @@
+package server
+
+import (
+	"fmt"
+	"strings"
+
+	"github.com/bio-routing/bio-rd/protocols/bgp/packet"
+	"github.com/bio-routing/bio-rd/route"
+)
+
+func pathAttribues(p *route.Path, fsm *FSM) (*packet.PathAttribute, error) {
+	asPathPA, err := packet.ParseASPathStr(strings.TrimRight(fmt.Sprintf("%d %s", fsm.localASN, p.BGPPath.ASPath), " "))
+	if err != nil {
+		return nil, fmt.Errorf("Unable to parse AS path: %v", err)
+	}
+
+	origin := &packet.PathAttribute{
+		TypeCode: packet.OriginAttr,
+		Value:    p.BGPPath.Origin,
+		Next:     asPathPA,
+	}
+
+	nextHop := &packet.PathAttribute{
+		TypeCode: packet.NextHopAttr,
+		Value:    p.BGPPath.NextHop,
+	}
+	asPathPA.Next = nextHop
+
+	localPref := &packet.PathAttribute{
+		TypeCode: packet.LocalPrefAttr,
+		Value:    p.BGPPath.LocalPref,
+	}
+	nextHop.Next = localPref
+
+	if p.BGPPath != nil {
+		err := addOptionalPathAttribues(p, localPref)
+
+		if err != nil {
+			return nil, err
+		}
+	}
+
+	return origin, nil
+}
+
+func addOptionalPathAttribues(p *route.Path, parent *packet.PathAttribute) error {
+	current := parent
+
+	if len(p.BGPPath.LargeCommunities) > 0 {
+		largeCommunities, err := packet.LargeCommunityAttributeForString(p.BGPPath.LargeCommunities)
+		if err != nil {
+			return fmt.Errorf("Could not create large community attribute: %v", err)
+		}
+
+		current.Next = largeCommunities
+		current = largeCommunities
+	}
+
+	return nil
+}
diff --git a/protocols/bgp/server/update_sender.go b/protocols/bgp/server/update_sender.go
index c33f2ce01882e0ba1326962fce7396c9005d9aae..54c1d1b0cfa28c9094015d50483af1fd12bfd2e6 100644
--- a/protocols/bgp/server/update_sender.go
+++ b/protocols/bgp/server/update_sender.go
@@ -28,31 +28,18 @@ func newUpdateSender(fsm *FSM) *UpdateSender {
 
 // AddPath serializes a new path and sends out a BGP update message
 func (u *UpdateSender) AddPath(pfx net.Prefix, p *route.Path) error {
-	asPathPA, err := packet.ParseASPathStr(asPathString(u.iBGP, u.fsm.localASN, p.BGPPath.ASPath))
+	pathAttrs, err := pathAttribues(p, u.fsm)
 	if err != nil {
-		return fmt.Errorf("Unable to parse AS path: %v", err)
+		log.Errorf("Unable to create BGP Update: %v", err)
+		return nil
 	}
 
-	update := &packet.BGPUpdate{
-		PathAttributes: &packet.PathAttribute{
-			TypeCode: packet.OriginAttr,
-			Value:    p.BGPPath.Origin,
-			Next: &packet.PathAttribute{
-				TypeCode: packet.ASPathAttr,
-				Value:    asPathPA.Value,
-				Next: &packet.PathAttribute{
-					TypeCode: packet.NextHopAttr,
-					Value:    p.BGPPath.NextHop,
-					Next: &packet.PathAttribute{
-						TypeCode: packet.LocalPrefAttr,
-						Value:    p.BGPPath.LocalPref,
-					},
-				},
-			},
-		},
-		NLRI: &packet.NLRI{
-			IP:     pfx.Addr(),
-			Pfxlen: pfx.Pfxlen(),
+	update := &packet.BGPUpdateAddPath{
+		PathAttributes: pathAttrs,
+		NLRI: &packet.NLRIAddPath{
+			PathIdentifier: p.BGPPath.PathIdentifier,
+			IP:             pfx.Addr(),
+			Pfxlen:         pfx.Pfxlen(),
 		},
 	}
 
diff --git a/protocols/bgp/server/update_sender_add_path.go b/protocols/bgp/server/update_sender_add_path.go
index a0fe2b3046174cfd5abd929366f631fb07c4d4fc..84d6796046b8b3380a6afae2f7ab1257b59a699c 100644
--- a/protocols/bgp/server/update_sender_add_path.go
+++ b/protocols/bgp/server/update_sender_add_path.go
@@ -27,32 +27,17 @@ func newUpdateSenderAddPath(fsm *FSM) *UpdateSenderAddPath {
 
 // AddPath serializes a new path and sends out a BGP update message
 func (u *UpdateSenderAddPath) AddPath(pfx net.Prefix, p *route.Path) error {
-	asPathPA, err := packet.ParseASPathStr(asPathString(u.iBGP, u.fsm.localASN, p.BGPPath.ASPath))
+	pathAttrs, err := pathAttribues(p, u.fsm)
 	if err != nil {
-		return fmt.Errorf("Unable to parse AS path: %v", err)
+		log.Errorf("Unable to create BGP Update: %v", err)
+		return nil
 	}
 
-	update := &packet.BGPUpdateAddPath{
-		PathAttributes: &packet.PathAttribute{
-			TypeCode: packet.OriginAttr,
-			Value:    p.BGPPath.Origin,
-			Next: &packet.PathAttribute{
-				TypeCode: packet.ASPathAttr,
-				Value:    asPathPA.Value,
-				Next: &packet.PathAttribute{
-					TypeCode: packet.NextHopAttr,
-					Value:    p.BGPPath.NextHop,
-					Next: &packet.PathAttribute{
-						TypeCode: packet.LocalPrefAttr,
-						Value:    p.BGPPath.LocalPref,
-					},
-				},
-			},
-		},
-		NLRI: &packet.NLRIAddPath{
-			PathIdentifier: p.BGPPath.PathIdentifier,
-			IP:             pfx.Addr(),
-			Pfxlen:         pfx.Pfxlen(),
+	update := &packet.BGPUpdate{
+		PathAttributes: pathAttrs,
+		NLRI: &packet.NLRI{
+			IP:     pfx.Addr(),
+			Pfxlen: pfx.Pfxlen(),
 		},
 	}
 
diff --git a/route/bgp.go b/route/bgp.go
index 847964f913645397ffbab534c73cafd3f7b0d9c9..a19c4cde94873f5761fd579737bacde3fd240729 100644
--- a/route/bgp.go
+++ b/route/bgp.go
@@ -10,16 +10,17 @@ import (
 
 // BGPPath represents a set of BGP path attributes
 type BGPPath struct {
-	PathIdentifier uint32
-	NextHop        uint32
-	LocalPref      uint32
-	ASPath         string
-	ASPathLen      uint16
-	Origin         uint8
-	MED            uint32
-	EBGP           bool
-	BGPIdentifier  uint32
-	Source         uint32
+	PathIdentifier   uint32
+	NextHop          uint32
+	LocalPref        uint32
+	ASPath           string
+	ASPathLen        uint16
+	Origin           uint8
+	MED              uint32
+	EBGP             bool
+	BGPIdentifier    uint32
+	Source           uint32
+	LargeCommunities string
 }
 
 // ECMP determines if routes b and c are euqal in terms of ECMP
diff --git a/routingtable/filter/actions/add_large_community_action.go b/routingtable/filter/actions/add_large_community_action.go
new file mode 100644
index 0000000000000000000000000000000000000000..17169c315817509c8c6c1a2b382ab39c3b5f0428
--- /dev/null
+++ b/routingtable/filter/actions/add_large_community_action.go
@@ -0,0 +1,34 @@
+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
+}
+
+func NewAddLargeCommunityAction(coms []*packet.LargeCommunity) *AddLargeCommunityAction {
+	return &AddLargeCommunityAction{
+		communities: coms,
+	}
+}
+
+func (a *AddLargeCommunityAction) Do(p net.Prefix, pa *route.Path) (modPath *route.Path, reject bool) {
+	if pa.BGPPath == nil || len(a.communities) == 0 {
+		return pa, false
+	}
+
+	modified := pa.Copy()
+
+	for _, com := range a.communities {
+		modified.BGPPath.LargeCommunities = modified.BGPPath.LargeCommunities + " " + com.String()
+	}
+	modified.BGPPath.LargeCommunities = strings.TrimLeft(modified.BGPPath.LargeCommunities, " ")
+
+	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
new file mode 100644
index 0000000000000000000000000000000000000000..d2c597fd5cbe8011b3eeb7b74443a09c726d6eb7
--- /dev/null
+++ b/routingtable/filter/actions/add_large_community_action_test.go
@@ -0,0 +1,75 @@
+package actions
+
+import (
+	"testing"
+
+	"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"
+)
+
+func TestAddingLargeCommunities(t *testing.T) {
+	tests := []struct {
+		name        string
+		current     string
+		communities []*packet.LargeCommunity
+		expected    string
+	}{
+		{
+			name: "add one to empty",
+			communities: []*packet.LargeCommunity{
+				&packet.LargeCommunity{
+					GlobalAdministrator: 1,
+					DataPart1:           2,
+					DataPart2:           3,
+				},
+			},
+			expected: "(1,2,3)",
+		},
+		{
+			name:    "add one to existing",
+			current: "(5,6,7)",
+			communities: []*packet.LargeCommunity{
+				&packet.LargeCommunity{
+					GlobalAdministrator: 1,
+					DataPart1:           2,
+					DataPart2:           3,
+				},
+			},
+			expected: "(5,6,7) (1,2,3)",
+		},
+		{
+			name:    "add two to existing",
+			current: "(5,6,7)",
+			communities: []*packet.LargeCommunity{
+				&packet.LargeCommunity{
+					GlobalAdministrator: 1,
+					DataPart1:           2,
+					DataPart2:           3,
+				},
+				&packet.LargeCommunity{
+					GlobalAdministrator: 7,
+					DataPart1:           8,
+					DataPart2:           9,
+				},
+			},
+			expected: "(5,6,7) (1,2,3) (7,8,9)",
+		},
+	}
+
+	for _, test := range tests {
+		t.Run(test.name, func(te *testing.T) {
+			p := &route.Path{
+				BGPPath: &route.BGPPath{
+					LargeCommunities: test.current,
+				},
+			}
+
+			a := NewAddLargeCommunityAction(test.communities)
+			modPath, _ := a.Do(net.Prefix{}, p)
+
+			assert.Equal(te, test.expected, modPath.BGPPath.LargeCommunities)
+		})
+	}
+}
diff --git a/routingtable/filter/large_community_filter.go b/routingtable/filter/large_community_filter.go
new file mode 100644
index 0000000000000000000000000000000000000000..0f9d474e69728ba682cde441e819a0d08bd7cc77
--- /dev/null
+++ b/routingtable/filter/large_community_filter.go
@@ -0,0 +1,15 @@
+package filter
+
+import (
+	"strings"
+
+	"github.com/bio-routing/bio-rd/protocols/bgp/packet"
+)
+
+type LargeCommunityFilter struct {
+	community *packet.LargeCommunity
+}
+
+func (f *LargeCommunityFilter) Matches(communityString string) bool {
+	return strings.Contains(communityString, f.community.String())
+}
diff --git a/routingtable/filter/term_condition.go b/routingtable/filter/term_condition.go
index 8bc87485d58bd9215d122e4af9792c7c0c1c2b82..003b9dcbc021e75cd28d3039efbad2d00bf1d109 100644
--- a/routingtable/filter/term_condition.go
+++ b/routingtable/filter/term_condition.go
@@ -6,8 +6,9 @@ import (
 )
 
 type TermCondition struct {
-	prefixLists  []*PrefixList
-	routeFilters []*RouteFilter
+	prefixLists           []*PrefixList
+	routeFilters          []*RouteFilter
+	largeCommunityFilters []*LargeCommunityFilter
 }
 
 func NewTermCondition(prefixLists []*PrefixList, routeFilters []*RouteFilter) *TermCondition {
@@ -18,7 +19,7 @@ func NewTermCondition(prefixLists []*PrefixList, routeFilters []*RouteFilter) *T
 }
 
 func (f *TermCondition) Matches(p net.Prefix, pa *route.Path) bool {
-	return f.matchesAnyPrefixList(p) || f.machtchesAnyRouteFilter(p)
+	return f.matchesAnyPrefixList(p) || f.machtchesAnyRouteFilter(p) || f.machtchesAnyLageCommunityFilter(pa)
 }
 
 func (t *TermCondition) matchesAnyPrefixList(p net.Prefix) bool {
@@ -40,3 +41,17 @@ func (t *TermCondition) machtchesAnyRouteFilter(p net.Prefix) bool {
 
 	return false
 }
+
+func (t *TermCondition) machtchesAnyLageCommunityFilter(pa *route.Path) bool {
+	if pa.BGPPath == nil {
+		return false
+	}
+
+	for _, l := range t.largeCommunityFilters {
+		if l.Matches(pa.BGPPath.LargeCommunities) {
+			return true
+		}
+	}
+
+	return false
+}
diff --git a/routingtable/filter/term_condition_test.go b/routingtable/filter/term_condition_test.go
index fba97b673bea6901983e6bba509358fe8bf504f3..6e9b89f20eb226faebdd3856fa4bc61a97388a4e 100644
--- a/routingtable/filter/term_condition_test.go
+++ b/routingtable/filter/term_condition_test.go
@@ -4,17 +4,20 @@ import (
 	"testing"
 
 	"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"
 )
 
 func TestMatches(t *testing.T) {
 	tests := []struct {
-		name         string
-		prefix       net.Prefix
-		prefixLists  []*PrefixList
-		routeFilters []*RouteFilter
-		expected     bool
+		name                  string
+		prefix                net.Prefix
+		bgpPath               *route.BGPPath
+		prefixLists           []*PrefixList
+		routeFilters          []*RouteFilter
+		largeCommunityFilters []*LargeCommunityFilter
+		expected              bool
 	}{
 		{
 			name:   "one prefix matches in prefix list, no route filters set",
@@ -105,16 +108,50 @@ func TestMatches(t *testing.T) {
 			},
 			expected: true,
 		},
+		{
+			name:   "large community matches",
+			prefix: net.NewPfx(strAddr("10.0.0.0"), 24),
+			bgpPath: &route.BGPPath{
+				LargeCommunities: "(1,2,0) (1,2,3)",
+			},
+			largeCommunityFilters: []*LargeCommunityFilter{
+				{
+					&packet.LargeCommunity{
+						GlobalAdministrator: 1,
+						DataPart1:           2,
+						DataPart2:           3,
+					},
+				},
+			},
+			expected: true,
+		},
+		{
+			name:    "large community does not match",
+			prefix:  net.NewPfx(strAddr("10.0.0.0"), 24),
+			bgpPath: &route.BGPPath{},
+			largeCommunityFilters: []*LargeCommunityFilter{
+				{
+					&packet.LargeCommunity{
+						GlobalAdministrator: 1,
+						DataPart1:           2,
+						DataPart2:           3,
+					},
+				},
+			},
+			expected: false,
+		},
 	}
 
 	for _, test := range tests {
 		t.Run(test.name, func(te *testing.T) {
-			f := NewTermCondition(
-				test.prefixLists,
-				test.routeFilters,
-			)
+			f := NewTermCondition(test.prefixLists, test.routeFilters)
+			f.largeCommunityFilters = test.largeCommunityFilters
+
+			pa := &route.Path{
+				BGPPath: test.bgpPath,
+			}
 
-			assert.Equal(te, test.expected, f.Matches(test.prefix, &route.Path{}))
+			assert.Equal(te, test.expected, f.Matches(test.prefix, pa))
 		})
 	}
 }