diff --git a/protocols/bgp/packet/community.go b/protocols/bgp/packet/community.go
index 6f4e2a67ab3b074117211e28a84d6da2188da3d9..05b3d8fcb15d295df75853067eea5e124b1e47b2 100644
--- a/protocols/bgp/packet/community.go
+++ b/protocols/bgp/packet/community.go
@@ -13,7 +13,7 @@ const (
 
 func CommunityStringForUint32(v uint32) string {
 	e1 := v >> 16
-	e2 := v - e1<<16
+	e2 := v & 0x0000FFFF
 
 	return fmt.Sprintf("(%d,%d)", e1, e2)
 }
diff --git a/routingtable/filter/actions/add_community_action.go b/routingtable/filter/actions/add_community_action.go
new file mode 100644
index 0000000000000000000000000000000000000000..01ad05f62936574bb576d55fe1c7bcf23697a38f
--- /dev/null
+++ b/routingtable/filter/actions/add_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 AddCommunityAction struct {
+	communities []uint32
+}
+
+func NewAddCommunityAction(coms []uint32) *AddCommunityAction {
+	return &AddCommunityAction{
+		communities: coms,
+	}
+}
+
+func (a *AddCommunityAction) 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.Communities = modified.BGPPath.Communities + " " + packet.CommunityStringForUint32(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
new file mode 100644
index 0000000000000000000000000000000000000000..0687e86c4bc80b8648d172cb125449962dea9cf6
--- /dev/null
+++ b/routingtable/filter/actions/add_community_action_test.go
@@ -0,0 +1,57 @@
+package actions
+
+import (
+	"testing"
+
+	"github.com/bio-routing/bio-rd/net"
+	"github.com/bio-routing/bio-rd/route"
+	"github.com/stretchr/testify/assert"
+)
+
+func TestAddingCommunities(t *testing.T) {
+	tests := []struct {
+		name        string
+		current     string
+		communities []uint32
+		expected    string
+	}{
+		{
+			name: "add one to empty",
+			communities: []uint32{
+				65538,
+			},
+			expected: "(1,2)",
+		},
+		{
+			name:    "add one to existing",
+			current: "(1,2)",
+			communities: []uint32{
+				196612,
+			},
+			expected: "(1,2) (3,4)",
+		},
+		{
+			name:    "add two to existing",
+			current: "(1,2)",
+			communities: []uint32{
+				196612, 327686,
+			},
+			expected: "(1,2) (3,4) (5,6)",
+		},
+	}
+
+	for _, test := range tests {
+		t.Run(test.name, func(te *testing.T) {
+			p := &route.Path{
+				BGPPath: &route.BGPPath{
+					Communities: test.current,
+				},
+			}
+
+			a := NewAddCommunityAction(test.communities)
+			modPath, _ := a.Do(net.Prefix{}, p)
+
+			assert.Equal(te, test.expected, modPath.BGPPath.Communities)
+		})
+	}
+}
diff --git a/routingtable/filter/community_filter.go b/routingtable/filter/community_filter.go
new file mode 100644
index 0000000000000000000000000000000000000000..9d384e54ad0a58ee622e3d2b0bb1b857c867b0c3
--- /dev/null
+++ b/routingtable/filter/community_filter.go
@@ -0,0 +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))
+}
diff --git a/routingtable/filter/term_condition.go b/routingtable/filter/term_condition.go
index 003b9dcbc021e75cd28d3039efbad2d00bf1d109..fe19e461701dc1a453a3ddee17a38547d08936a6 100644
--- a/routingtable/filter/term_condition.go
+++ b/routingtable/filter/term_condition.go
@@ -8,6 +8,7 @@ import (
 type TermCondition struct {
 	prefixLists           []*PrefixList
 	routeFilters          []*RouteFilter
+	communityFilters      []*CommunityFilter
 	largeCommunityFilters []*LargeCommunityFilter
 }
 
@@ -19,10 +20,17 @@ 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) || f.machtchesAnyLageCommunityFilter(pa)
+	return f.matchesPrefixListFilters(p) &&
+		f.machtchesRouteFilters(p) &&
+		f.machtchesCommunityFilters(pa) &&
+		f.machtchesLargeCommunityFilters(pa)
 }
 
-func (t *TermCondition) matchesAnyPrefixList(p net.Prefix) bool {
+func (t *TermCondition) matchesPrefixListFilters(p net.Prefix) bool {
+	if len(t.prefixLists) == 0 {
+		return true
+	}
+
 	for _, l := range t.prefixLists {
 		if l.Matches(p) {
 			return true
@@ -32,7 +40,11 @@ func (t *TermCondition) matchesAnyPrefixList(p net.Prefix) bool {
 	return false
 }
 
-func (t *TermCondition) machtchesAnyRouteFilter(p net.Prefix) bool {
+func (t *TermCondition) machtchesRouteFilters(p net.Prefix) bool {
+	if len(t.routeFilters) == 0 {
+		return true
+	}
+
 	for _, l := range t.routeFilters {
 		if l.Matches(p) {
 			return true
@@ -42,7 +54,29 @@ func (t *TermCondition) machtchesAnyRouteFilter(p net.Prefix) bool {
 	return false
 }
 
-func (t *TermCondition) machtchesAnyLageCommunityFilter(pa *route.Path) bool {
+func (t *TermCondition) machtchesCommunityFilters(pa *route.Path) bool {
+	if len(t.communityFilters) == 0 {
+		return true
+	}
+
+	if pa.BGPPath == nil {
+		return false
+	}
+
+	for _, l := range t.communityFilters {
+		if l.Matches(pa.BGPPath.Communities) {
+			return true
+		}
+	}
+
+	return false
+}
+
+func (t *TermCondition) machtchesLargeCommunityFilters(pa *route.Path) bool {
+	if len(t.largeCommunityFilters) == 0 {
+		return true
+	}
+
 	if pa.BGPPath == nil {
 		return false
 	}
diff --git a/routingtable/filter/term_condition_test.go b/routingtable/filter/term_condition_test.go
index 6e9b89f20eb226faebdd3856fa4bc61a97388a4e..cca90950aa5a443c042c8061f77a08d3f4f7a0c4 100644
--- a/routingtable/filter/term_condition_test.go
+++ b/routingtable/filter/term_condition_test.go
@@ -16,6 +16,7 @@ func TestMatches(t *testing.T) {
 		bgpPath               *route.BGPPath
 		prefixLists           []*PrefixList
 		routeFilters          []*RouteFilter
+		communityFilters      []*CommunityFilter
 		largeCommunityFilters []*LargeCommunityFilter
 		expected              bool
 	}{
@@ -25,8 +26,7 @@ func TestMatches(t *testing.T) {
 			prefixLists: []*PrefixList{
 				NewPrefixList(net.NewPfx(strAddr("127.0.0.1"), 8)),
 			},
-			routeFilters: []*RouteFilter{},
-			expected:     true,
+			expected: true,
 		},
 		{
 			name:   "one prefix in prefix list and no match, no route filters set",
@@ -34,8 +34,7 @@ func TestMatches(t *testing.T) {
 			prefixLists: []*PrefixList{
 				NewPrefixList(net.NewPfx(0, 32)),
 			},
-			routeFilters: []*RouteFilter{},
-			expected:     false,
+			expected: false,
 		},
 		{
 			name:   "one prefix of 2 matches in prefix list, no route filters set",
@@ -44,22 +43,19 @@ func TestMatches(t *testing.T) {
 				NewPrefixList(net.NewPfx(strAddr("10.0.0.0"), 8)),
 				NewPrefixList(net.NewPfx(strAddr("127.0.0.1"), 8)),
 			},
-			routeFilters: []*RouteFilter{},
-			expected:     true,
+			expected: true,
 		},
 		{
-			name:        "no prefixes in prefix list, only route filter matches",
-			prefix:      net.NewPfx(strAddr("10.0.0.0"), 24),
-			prefixLists: []*PrefixList{},
+			name:   "no prefixes in prefix list, only route filter matches",
+			prefix: net.NewPfx(strAddr("10.0.0.0"), 24),
 			routeFilters: []*RouteFilter{
 				NewRouteFilter(net.NewPfx(strAddr("10.0.0.0"), 8), Longer()),
 			},
 			expected: true,
 		},
 		{
-			name:        "no prefixes in prefix list, one route filter matches",
-			prefix:      net.NewPfx(strAddr("10.0.0.0"), 24),
-			prefixLists: []*PrefixList{},
+			name:   "no prefixes in prefix list, one route filter matches",
+			prefix: net.NewPfx(strAddr("10.0.0.0"), 24),
 			routeFilters: []*RouteFilter{
 				NewRouteFilter(net.NewPfx(strAddr("8.0.0.0"), 8), Longer()),
 				NewRouteFilter(net.NewPfx(strAddr("10.0.0.0"), 8), Longer()),
@@ -67,9 +63,8 @@ func TestMatches(t *testing.T) {
 			expected: true,
 		},
 		{
-			name:        "no prefixes in prefix list, one of many route filters matches",
-			prefix:      net.NewPfx(strAddr("127.0.0.1"), 8),
-			prefixLists: []*PrefixList{},
+			name:   "no prefixes in prefix list, one of many route filters matches",
+			prefix: net.NewPfx(strAddr("127.0.0.1"), 8),
 			routeFilters: []*RouteFilter{
 				NewRouteFilter(net.NewPfx(strAddr("10.0.0.0"), 8), Longer()),
 			},
@@ -87,7 +82,7 @@ func TestMatches(t *testing.T) {
 			expected: false,
 		},
 		{
-			name:   "one prefix in prefixlist, one route fitler, only prefix list matches",
+			name:   "one prefix in prefixlist, one route filter, only prefix list matches",
 			prefix: net.NewPfx(strAddr("8.8.8.0"), 24),
 			prefixLists: []*PrefixList{
 				NewPrefixList(net.NewPfx(strAddr("8.0.0.0"), 8)),
@@ -95,10 +90,10 @@ func TestMatches(t *testing.T) {
 			routeFilters: []*RouteFilter{
 				NewRouteFilter(net.NewPfx(strAddr("10.0.0.0"), 8), Longer()),
 			},
-			expected: true,
+			expected: false,
 		},
 		{
-			name:   "one prefix in prefixlist, one route fitler, only route filter matches",
+			name:   "one prefix in prefixlist, one route filter, only route filter matches",
 			prefix: net.NewPfx(strAddr("10.0.0.0"), 24),
 			prefixLists: []*PrefixList{
 				NewPrefixList(net.NewPfx(strAddr("8.0.0.0"), 8)),
@@ -106,8 +101,38 @@ func TestMatches(t *testing.T) {
 			routeFilters: []*RouteFilter{
 				NewRouteFilter(net.NewPfx(strAddr("10.0.0.0"), 8), Longer()),
 			},
+			expected: false,
+		},
+		{
+			name:   "community matches",
+			prefix: net.NewPfx(strAddr("10.0.0.0"), 24),
+			bgpPath: &route.BGPPath{
+				Communities: "(1,2) (3,4) (5,6)",
+			},
+			communityFilters: []*CommunityFilter{
+				&CommunityFilter{196612}, // (3,4)
+			},
 			expected: true,
 		},
+		{
+			name:   "community does not match",
+			prefix: net.NewPfx(strAddr("10.0.0.0"), 24),
+			bgpPath: &route.BGPPath{
+				Communities: "(1,2) (3,4) (5,6)",
+			},
+			communityFilters: []*CommunityFilter{
+				&CommunityFilter{196608}, // (3,0)
+			},
+			expected: false,
+		},
+		{
+			name:   "community filter, bgp path is nil",
+			prefix: net.NewPfx(strAddr("10.0.0.0"), 24),
+			communityFilters: []*CommunityFilter{
+				&CommunityFilter{196608}, // (3,0)
+			},
+			expected: false,
+		},
 		{
 			name:   "large community matches",
 			prefix: net.NewPfx(strAddr("10.0.0.0"), 24),
@@ -140,11 +165,26 @@ func TestMatches(t *testing.T) {
 			},
 			expected: false,
 		},
+		{
+			name:   "large community filter, bgp path is nil",
+			prefix: net.NewPfx(strAddr("10.0.0.0"), 24),
+			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.communityFilters = test.communityFilters
 			f.largeCommunityFilters = test.largeCommunityFilters
 
 			pa := &route.Path{