diff --git a/protocols/bgp/packet/bgp.go b/protocols/bgp/packet/bgp.go
index 41a2113f8e6cb85cb0250d0333908071dc03fd79..d59149a9d50360b698edf8d9a2f2066fb8ef11de 100644
--- a/protocols/bgp/packet/bgp.go
+++ b/protocols/bgp/packet/bgp.go
@@ -1,5 +1,9 @@
 package packet
 
+import (
+	"fmt"
+)
+
 const (
 	OctetLen       = 8
 	MaxASNsSegment = 255
@@ -51,13 +55,14 @@ const (
 	MalformedASPath           = 11
 
 	// Attribute Type Codes
-	OriginAttr     = 1
-	ASPathAttr     = 2
-	NextHopAttr    = 3
-	MEDAttr        = 4
-	LocalPrefAttr  = 5
-	AtomicAggrAttr = 6
-	AggregatorAttr = 7
+	OriginAttr         = 1
+	ASPathAttr         = 2
+	NextHopAttr        = 3
+	MEDAttr            = 4
+	LocalPrefAttr      = 5
+	AtomicAggrAttr     = 6
+	AggregatorAttr     = 7
+	LargeCommunityAttr = 32
 
 	// ORIGIN values
 	IGP        = 0
@@ -172,3 +177,13 @@ type Aggretator struct {
 	Addr uint32
 	ASN  uint16
 }
+
+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)
+}
diff --git a/protocols/bgp/packet/decoder_test.go b/protocols/bgp/packet/decoder_test.go
index 01d4e01596040cdab68d7cd5abdfb30b846fe3fa..4cbc8c110b507ce475c7b7f22ac3d73ba43992f1 100644
--- a/protocols/bgp/packet/decoder_test.go
+++ b/protocols/bgp/packet/decoder_test.go
@@ -852,7 +852,6 @@ func TestDecodeUpdateMsg(t *testing.T) {
 				5,          // Attribute Type code (Local Pref)
 				4,          // Length
 				0, 0, 1, 0, // Local Pref 256
-
 			},
 			wantFail: false,
 			expected: &BGPUpdate{
diff --git a/protocols/bgp/packet/path_attributes.go b/protocols/bgp/packet/path_attributes.go
index 0390b6617944b5ea1933b542b97c29ac069c52c6..4dc6c49c9d50c94698b44eb953b11d9d5a8a4a39 100644
--- a/protocols/bgp/packet/path_attributes.go
+++ b/protocols/bgp/packet/path_attributes.go
@@ -84,6 +84,10 @@ func decodePathAttr(buf *bytes.Buffer) (pa *PathAttribute, consumed uint16, err
 		}
 	case AtomicAggrAttr:
 		// Nothing to do for 0 octet long attribute
+	case LargeCommunityAttr:
+		if err := pa.decodeLargeCommunities(buf); err != nil {
+			return nil, consumed, fmt.Errorf("Failed to decode large communities: %v", err)
+		}
 	default:
 		return nil, consumed, fmt.Errorf("Invalid Attribute Type Code: %v", pa.TypeCode)
 	}
@@ -210,6 +214,43 @@ func (pa *PathAttribute) decodeAggregator(buf *bytes.Buffer) error {
 	return dumpNBytes(buf, pa.Length-p)
 }
 
+func (pa *PathAttribute) decodeLargeCommunities(buf *bytes.Buffer) error {
+	length := pa.Length
+	recordLen := uint16(12)
+	count := length / recordLen
+
+	coms := make([]LargeCommunity, count)
+
+	for i := uint16(0); i < count; i++ {
+		com := LargeCommunity{}
+
+		v, err := read4BytesAsUin32(buf)
+		if err != nil {
+			return err
+		}
+		com.GlobalAdministrator = v
+
+		v, err = read4BytesAsUin32(buf)
+		if err != nil {
+			return err
+		}
+		com.DataPart1 = v
+
+		v, err = read4BytesAsUin32(buf)
+		if err != nil {
+			return err
+		}
+		com.DataPart2 = v
+
+		coms[i] = com
+	}
+
+	pa.Value = coms
+
+	dump := pa.Length - (count * recordLen)
+	return dumpNBytes(buf, dump)
+}
+
 func (pa *PathAttribute) setLength(buf *bytes.Buffer) (int, error) {
 	bytesRead := 0
 	if pa.ExtendedLength {
@@ -281,6 +322,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 {
@@ -500,3 +550,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 read4BytesAsUin32(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 next hop: buf.Read read %d bytes", n)
+	}
+
+	return fourBytesToUint32(b), nil
+}
diff --git a/protocols/bgp/packet/path_attributes_test.go b/protocols/bgp/packet/path_attributes_test.go
index 15f78fe39a763b86eb31281cbee5e30e54a84585..34c62b49e0d569375a1b280e8ef6fc1f3088904a 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
@@ -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
diff --git a/protocols/bgp/server/fsm.go b/protocols/bgp/server/fsm.go
index c53696fb5e6895c165e56f75d38a3e441fd9449e..7681a127f17d6e4229d3ea79af22c55665a31831 100644
--- a/protocols/bgp/server/fsm.go
+++ b/protocols/bgp/server/fsm.go
@@ -843,6 +843,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/route/bgp.go b/route/bgp.go
index e56b140b4bea79f00125a020c5f05b61aceb7279..1109a42d2b8ee12cd4d1b7719ee402109b33085f 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