--- lib/type.h
+++ lib/type.h
@@ -12,6 +12,11 @@
 #include "lib/birdlib.h"
 #include "lib/attrs.h"
 
+struct f_aggregator {
+  u32 asn;
+  u32 rid;
+};
+
 union bval {
 #define BVAL_ITEMS									\
   struct {										\
@@ -36,6 +41,7 @@
   u64 ec;
   lcomm lc;
   vpn_rd rd;
+  struct f_aggregator aggregator;
   ip_addr ip;
   const net_addr *net;
   const char *s;
@@ -87,6 +93,7 @@
 /* Other user visible types which fit in int */
   T_BOOL = 0xa0,
   T_PAIR = 0xa4,  /*	Notice that pair is stored as integer: first << 16 | second */
+  T_AGGREGATOR = 0xa8,	/* BGP aggregator tuple: ASN + router ID */
 
 /* Put enumerational types in 0x20..0x3f range */
   T_ENUM_LO = 0x12,
--- filter/data.c
+++ filter/data.c
@@ -36,6 +36,7 @@
   [T_INT]	= "int",
   [T_BOOL]	= "bool",
   [T_PAIR]	= "pair",
+  [T_AGGREGATOR] = "aggregator",
   [T_QUAD]	= "quad",
 
   [T_ENUM_RTS]	= "enum rts",
@@ -159,6 +160,16 @@
   return 0;
 }
 
+static inline int
+aggregator_cmp(struct f_aggregator v1, struct f_aggregator v2)
+{
+  if (v1.asn != v2.asn)
+    return (v1.asn > v2.asn) ? 1 : -1;
+  if (v1.rid != v2.rid)
+    return (v1.rid > v2.rid) ? 1 : -1;
+  return 0;
+}
+
 /**
  * val_compare - compare two values
  * @v1: first value
@@ -201,6 +212,8 @@
     return u64_cmp(v1->val.ec, v2->val.ec);
   case T_LC:
     return lcomm_cmp(v1->val.lc, v2->val.lc);
+  case T_AGGREGATOR:
+    return aggregator_cmp(v1->val.aggregator, v2->val.aggregator);
   case T_IP:
     return ipa_compare(v1->val.ip, v2->val.ip);
   case T_NET:
@@ -612,6 +625,9 @@
     case T_ENUM:
       MX(i);
       break;
+    case T_AGGREGATOR:
+      MX(aggregator);
+      break;
     case T_EC:
     case T_RD:
       MX(ec);
@@ -705,6 +721,7 @@
   case T_IP:	buffer_print(buf, "%I", v->val.ip); return;
   case T_NET:   buffer_print(buf, "%N", v->val.net); return;
   case T_PAIR:	buffer_print(buf, "(%u,%u)", v->val.i >> 16, v->val.i & 0xffff); return;
+  case T_AGGREGATOR: buffer_print(buf, "aggregator(%u, %R)", v->val.aggregator.asn, v->val.aggregator.rid); return;
   case T_QUAD:	buffer_print(buf, "%R", v->val.i); return;
   case T_EC:	ec_format(buf2, v->val.ec); buffer_print(buf, "%s", buf2); return;
   case T_LC:	lc_format(buf2, v->val.lc); buffer_print(buf, "%s", buf2); return;
@@ -754,6 +771,7 @@
     case T_INT:
     case T_IP:
     case T_PAIR:
+    case T_AGGREGATOR:
     case T_QUAD:
     case T_EC:
     case T_LC:
--- filter/config.Y
+++ filter/config.Y
@@ -374,6 +374,7 @@
 	ADD, DELETE, RESET,
 	PREPEND,
 	APPEND,
+	AGGREGATOR,
 	EMPTY,
 	FILTER, WHERE, EVAL, ATTRIBUTE,
 	FROM_HEX,
@@ -484,6 +485,7 @@
  | RD { $$ = T_RD; }
  | PREFIX { $$ = T_NET; }
  | PAIR { $$ = T_PAIR; }
+ | AGGREGATOR { $$ = T_AGGREGATOR; }
  | QUAD { $$ = T_QUAD; }
  | EC { $$ = T_EC; }
  | LC { $$ = T_LC; }
@@ -858,6 +860,7 @@
 
 constructor:
    '(' term ',' term ')' { $$ = f_new_inst(FI_PAIR_CONSTRUCT, $2, $4); }
+ | AGGREGATOR '(' term ',' term ')' { $$ = f_new_inst(FI_AGGREGATOR_CONSTRUCT, $3, $5); }
  | '(' ec_kind ',' term ',' term ')' { $$ = f_new_inst(FI_EC_CONSTRUCT, $4, $6, $2); }
  | '(' term ',' term ',' term ')' { $$ = f_new_inst(FI_LC_CONSTRUCT, $2, $4, $6); }
  | bgp_path { $$ = f_new_inst(FI_PATHMASK_CONSTRUCT, $1); }
--- filter/f-inst.c
+++ filter/f-inst.c
@@ -342,6 +342,22 @@
     RESULT(T_PAIR, i, (u1 << 16) | u2);
   }
 
+  INST(FI_AGGREGATOR_CONSTRUCT, 2, 1) {
+    ARG(1, T_INT);
+    ARG_ANY(2);
+
+    u32 rid;
+
+    if (v2.type == T_QUAD)
+      rid = v2.val.i;
+    else if (val_is_ip4(&v2))
+      rid = ipa_to_u32(v2.val.ip);
+    else
+      runtime("Argument 2 of aggregator constructor must be router ID or IPv4 address, got 0x%02x", v2.type);
+
+    RESULT(T_AGGREGATOR, aggregator, ((struct f_aggregator) { .asn = v1.val.i, .rid = rid }));
+  }
+
   INST(FI_EC_CONSTRUCT, 2, 1) {
     ARG_ANY(1);
     ARG(2, T_INT);
@@ -911,6 +927,14 @@
 	    RESULT_(T_IP, ip, *((const ip_addr *) e->u.ptr->data));
 	    break;
 
+	  case T_AGGREGATOR:
+	    RESULT_(T_AGGREGATOR, aggregator,
+		((struct f_aggregator) {
+		  .asn = get_u32(e->u.ptr->data + 0),
+		  .rid = get_u32(e->u.ptr->data + 4),
+		}));
+	    break;
+
 	  case T_STRING:
 	    RESULT_(T_STRING, s, (const char *) e->u.ptr->data);
 	    break;
@@ -956,6 +980,16 @@
 	    EA_LITERAL_STORE_ADATA(da, da->flags, &v1.val.ip, sizeof(ip_addr)));
 	break;
 
+      case T_AGGREGATOR:
+	{
+	  byte data[8];
+	  put_u32(data + 0, v1.val.aggregator.asn);
+	  put_u32(data + 4, v1.val.aggregator.rid);
+	  a = ea_set_attr(&fs->rte->attrs,
+	      EA_LITERAL_STORE_ADATA(da, da->flags, data, sizeof(data)));
+	}
+	break;
+
       case T_STRING:
 	a = ea_set_attr(&fs->rte->attrs,
 	    EA_LITERAL_STORE_ADATA(da, da->flags, v1.val.s, strlen(v1.val.s) + 1));
@@ -1117,6 +1151,12 @@
   /* Get data part from the standard community */
   METHOD_R(T_PAIR, data, T_INT, i, v1.val.i & 0xFFFF);
 
+  /* Get ASN part from aggregator */
+  METHOD_R(T_AGGREGATOR, asn, T_INT, i, v1.val.aggregator.asn);
+
+  /* Get router ID part from aggregator */
+  METHOD_R(T_AGGREGATOR, router_id, T_QUAD, i, v1.val.aggregator.rid);
+
   /* Get ASN part from the large community */
   METHOD_R(T_LC, asn, T_INT, i, v1.val.lc.asn);
 
--- proto/bgp/attrs.c
+++ proto/bgp/attrs.c
@@ -1111,7 +1111,7 @@
   [BA_AGGREGATOR] = {
     .name = "bgp_aggregator",
     .legacy_name = "BGP.aggregator",
-    .type = T_OPAQUE,
+    .type = T_AGGREGATOR,
     .flags = BAF_OPTIONAL | BAF_TRANSITIVE,
     .encode = bgp_encode_aggregator,
     .decode = bgp_decode_aggregator,
--- filter/test.conf
+++ filter/test.conf
@@ -1181,6 +1181,29 @@
 
 
 /*
+ *	Testing Aggregator
+ *	------------------
+ */
+
+function t_aggregator()
+aggregator a;
+{
+	a = aggregator(64512, 192.0.2.1);
+	bt_assert(format(a) = "aggregator(64512, 192.0.2.1)");
+	bt_assert(a.asn = 64512);
+	bt_assert(a.router_id = 192.0.2.1);
+
+	a = aggregator(64513, 10.20.30.40);
+	bt_assert(a.asn = 64513);
+	bt_assert(a.router_id = 10.20.30.40);
+}
+
+bt_test_suite(t_aggregator, "Testing aggregator");
+
+
+
+
+/*
  * 	Testing Community List
  *	----------------------
  */
@@ -2952,6 +2975,7 @@
 
 	bt_check_assign(from, 10.20.30.40);
 	# bt_check_assign(gw, 55.55.55.44);
+	bt_check_assign(bgp_aggregator, aggregator(64512, 10.20.30.40));
 
 	bgp_community.add((3,5));
 	bgp_ext_community.add((ro, 135, 999));
--- doc/bird.sgml
+++ doc/bird.sgml
@@ -4236,10 +4236,11 @@
 	presence of which indicates that the route has been aggregated from
 	multiple routes by some router on the path from the originator.
 
-	<tag><label id="rta-bgp-aggregator">void bgp_aggregator [O]</tag>
+	<tag><label id="rta-bgp-aggregator">aggregator bgp_aggregator [O]</tag>
 	This is an optional attribute specifying AS number and IP address of the
 	BGP router that created the route by aggregating multiple BGP routes.
-	Currently, the attribute is not accessible from filters.
+	It is accessible from filters and can be set using
+	<cf/aggregator(asn, router_id)/>.
 
 	<tag><label id="rta-bgp-community">clist bgp_community [O]</tag>
 	List of community values associated with the route. Each such value is a
