BIRD has variety of types, used to represent most common
attributes in protocols. But sometimes wery complex attributes
might be found. An good example of such attribute is BGP_AGGREGATOR
which consists from an INT, and QUAD types (ASN,ROUTER_ID).

Rather than making *special* type for aggregator, introduce general
infrastructure for structured types and implement aggregator
as STRUCT ASID with its literal format written as { ASN, ROUTER_ID }.
Variables of STRUCT ASID also supported.

Introduce additional operator for dereference structure members: ->,
So for example bgp_aggregator->'ai.as' gies an ASN number from
structure. Assignment to structure members also supported on both
extended attributes and variables.
---
 conf/cf-lex.l      |   30 +++++++++++
 conf/conf.h        |    2 +
 conf/confbase.Y    |    4 +-
 doc/bird.sgml      |   61 ++++++++++++++++++++--
 filter/config.Y    |   96 ++++++++++++++++++++++++++++++++++
 filter/f-util.c    |   91 ++++++++++++++++++++++++++++++--
 filter/filter.c    |  146
++++++++++++++++++++++++++++++++++++++++++++++++++++
 filter/filter.h    |   31 +++++++++--
 nest/route.h       |    1 +
 proto/bgp/attrs.c  |   14 +++--
 proto/bgp/config.Y |    2 +-
 11 files changed, 457 insertions(+), 21 deletions(-)

diff --git a/conf/cf-lex.l b/conf/cf-lex.l
index b1bbeae..5996f30 100644
--- a/conf/cf-lex.l
+++ b/conf/cf-lex.l
@@ -252,6 +252,7 @@ else: {
 \[\= return PO;
 \=\] return PC;
 +\-\> return REF;
 %%
  static int
@@ -512,6 +513,33 @@ cf_define_symbol(struct symbol *sym, int type, void
*def)
   return sym;
 }
 +/**
+ * cf_define_symbol_if_undef - redefine symbol with new meaning
+ * @sym: symbol to be defined
+ * @type: symbol class to assign
+ * @def: class dependent data
+ *
+ * Takes symbol and re-defines it to the new type if it's type was
+ * undefined previously (%SYM_VOID) or returns symbol unmodified
+ * if it's type same as specified with @type parameter, overwise
+ * an error is reported via cf_error().
+ */
+struct symbol *
+cf_define_symbol_if_undef(struct symbol *sym, int type, void *def)
+{
+  if (sym->class)
+    {
+      if (sym->class != type)
+        cf_error("Symbol already defined");
+    }
+  else
+    {
+      sym->class = type;
+      sym->def = def;
+    }
+  return sym;
+}
+
 static void
 cf_lex_init_kh(void)
 {
@@ -642,6 +670,8 @@ cf_symbol_class_name(struct symbol *sym)
       return "routing table";
     case SYM_ROA:
       return "ROA table";
+    case SYM_REF:
+      return "reference to struct member";
     default:
       return "unknown type";
     }
diff --git a/conf/conf.h b/conf/conf.h
index 2862429..5aaa1f3 100644
--- a/conf/conf.h
+++ b/conf/conf.h
@@ -115,6 +115,7 @@ struct symbol {
 #define SYM_FILTER 4
 #define SYM_TABLE 5
 #define SYM_ROA 6
+#define SYM_REF 255
  #define SYM_VARIABLE 0x100    /* 0x100-0x1ff are variable types */
 #define SYM_CONSTANT 0x200     /* 0x200-0x2ff are variable types */
@@ -141,6 +142,7 @@ void cf_lex_init(int is_cli, struct config *c);
 struct symbol *cf_find_symbol(byte *c);
 struct symbol *cf_default_name(char *template, int *counter);
 struct symbol *cf_define_symbol(struct symbol *symbol, int type, void
*def);
+struct symbol *cf_define_symbol_if_undef(struct symbol *sym, int type,
void *def);
 void cf_push_scope(struct symbol *);
 void cf_pop_scope(void);
 struct symbol *cf_walk_symbols(struct config *cf, struct symbol *sym,
int *pos);
diff --git a/conf/confbase.Y b/conf/confbase.Y
index c6678e7..f5f701f 100644
--- a/conf/confbase.Y
+++ b/conf/confbase.Y
@@ -62,7 +62,7 @@ CF_DECLS
   struct timeformat *tf;
 }
 -%token END CLI_MARKER INVALID_TOKEN ELSECOL DDOT
+%token END CLI_MARKER INVALID_TOKEN ELSECOL DDOT REF
 %token GEQ LEQ NEQ AND OR
 %token PO PC
 %token <i> NUM ENUM
@@ -84,7 +84,7 @@ CF_DECLS
 %left '+' '-'
 %left '*' '/' '%'
 %left '!'
-%nonassoc '.'
+%nonassoc '.' REF
  CF_KEYWORDS(DEFINE, ON, OFF, YES, NO)
 diff --git a/doc/bird.sgml b/doc/bird.sgml
index e4839a6..5791c0b 100644
--- a/doc/bird.sgml
+++ b/doc/bird.sgml
@@ -1049,6 +1049,60 @@ function is_bird_ipv6()
          variables of such type, but some route attributes are of enumeration
          type. Enumeration types are incompatible with each other.
 +      <tag/asid struct/
+         Structured types are supported in filters. Structure aggregates
fixed set of
+         objects with possibly different types in given order. Each object
within structure
+         has it's own label by which object can be referenced. Literals of
structured type look
+         like <cf/{ 1, 192.168.1.1, ... }/ and order of objects in literals
is important.
+         You can not define your own structures, but any literal of
predefined structure
+         could be used in configuration. Standard operations like assignment
to variable
+         of structured type, printing, comparison also supported.
+
+         There is special operator on structured type <cf/->/ which could be
used for
+         referencing labeled object in structured type. This allows
retrieving/assignment
+         of particular object in structured type.
+
+         BIRD defines following structured types:
+
+         <cf/asid/ represents AS number of integer type, followed by a router
ID of quad
+         type. There are two corresponding labels to reference each object
within structure:
+         <cf/ai.as/ refering to AS number object and <cf/ai.as/ refering to
router ID object.
+         This type is used to represent <cf/bgp_aggregator/ attribute.
+
+         There examples on how to work with structured types in filters:
+<code>
+define C_IP = 10.20.20.20;
+define C_BGP_AGGREGATOR = { 10, C_IP };
+
+function make_aggregator(int asn; quad router_id)
+asid struct ai;
+{
+       ai = { asn, router_id };
+       return ai;
+}
+
+function print_aggregator(asid struct ai)
+{
+       print "aggregator: ", ai;
+       print "ai.as: ", ai->'ai.as', ", ai.id: ", ai->'ai.id';
+       print "--";
+}
+
+function test_aggregator()
+{
+       print { 10, 10.10.10.10 }->'ai.as', ", ", { 10, 10.10.10.10 }->'ai.id';
+       print_aggregator({ 10, 10.10.10.10 });
+       print_aggregator(C_BGP_AGGREGATOR);
+
+       bgp_aggregator = { 10, 10.30.30.30 };
+       print_aggregator(bgp_aggregator);
+
+       bgp_aggregator->'ai.as' = 1;
+       bgp_aggregator->'ai.id' = 1.1.1.1;
+       print_aggregator(bgp_aggregator);
+}
+</code>
+
        <tag/bgppath/
          BGP path is a list of autonomous system numbers. You can't write
literals of this type.
          There are several special operators on bgppaths:
@@ -1705,9 +1759,10 @@ with `<tt/O/') are optional.
        has been aggregated from multiple routes by some router on the path from
        the originator.
 -<!-- we don't handle aggregators right since they are of a very
obscure type
-       <tag>bgp_aggregator</tag>
--->
+       <tag>asid struct bgp_aggregator</tag> This is an optional attribute
which carries
+       ASN and router ID that formed aggregated route. See <cf/asid struct/ 
type
+       description for more information on type of this attribute.
+
        <tag>clist <cf/bgp_community/ [O]</tag> List of community values
associated
        with the route. Each such value is a pair (represented as a <cf/pair/ 
data
        type inside the filters) of 16-bit integers, the first of them
containing the number of the AS which defines
diff --git a/filter/config.Y b/filter/config.Y
index a7a69a4..77b2446 100644
--- a/filter/config.Y
+++ b/filter/config.Y
@@ -250,6 +250,48 @@ f_generate_ec(u16 kind, struct f_inst *tk, struct
f_inst *tv)
   return rv;
 }
 +static inline struct f_inst *
+f_generate_ref(struct f_inst *t, struct symbol *s)
+{
+  struct f_inst *rv;
+
+  s = cf_define_symbol_if_undef(s, SYM_REF, NULL);
+
+  if (t->code == 'C') {
+    struct f_val res, *v = t->a1.p;
+
+    res.type = T_VOID;
+    switch (v->type) {
+    case T_STRUCT_ASID:
+      if (!strcmp(s->name, "ai.as")) {
+        res.type = T_INT;
+        res.val.i = v->val.ai.as;
+      } else if (!strcmp(s->name, "ai.id")) {
+        res.type = T_QUAD;
+        res.val.i = v->val.ai.id;
+      }
+      break;
+    default:
+      cf_error("Expected structured type: %x", v->type);
+    }
+
+    if (res.type == T_VOID)
+      cf_error("Unknown member in structure: %s", s->name);
+
+    NEW_F_VAL;
+    rv = f_new_inst();
+    rv->code = 'C';
+    rv->a1.p = val;
+    memcpy(val, &res, sizeof(*val));
+  } else {
+    rv = f_new_inst();
+    rv->code = P('s','s');
+    rv->a1.p = s;
+    rv->a2.p = t;
+  }
+
+  return rv;
+}
   CF_DECLS
@@ -259,6 +301,7 @@ CF_KEYWORDS(FUNCTION, PRINT, PRINTN, UNSET, RETURN,
        INT, BOOL, IP, PREFIX, PAIR, QUAD, EC,
        SET, STRING, BGPMASK, BGPPATH, CLIST, ECLIST,
        IF, THEN, ELSE, CASE, TRY, CATCH,
+       STRUCT, ASID,
        TRUE, FALSE, RT, RO, UNKNOWN, GENERIC,
        FROM, GW, NET, MASK, PROTO, SOURCE, SCOPE, CAST, DEST, IFNAME, IFINDEX,
        PREFERENCE,
@@ -313,6 +356,7 @@ type:
  | BGPPATH { $$ = T_PATH; }
  | CLIST { $$ = T_CLIST; }
  | ECLIST { $$ = T_ECLIST; }
+ | ASID STRUCT { $$ = T_STRUCT_ASID; }
  | type SET {          switch ($1) {
          case T_INT:
@@ -628,6 +672,7 @@ constant:
 constructor:
    '(' term ',' term ')' { $$ = f_generate_dpair($2, $4); }
  | '(' ec_kind ',' term ',' term ')' { $$ = f_generate_ec($2, $4, $6); }
+ | '{' term ',' term '}' { $$ = f_generate_struct(T_STRUCT_ASID, $2, $4); }
  ;
  @@ -719,6 +764,8 @@ term:
   | rtadot dynamic_attr { $$ = $2; $$->code = P('e','a'); }
 + | term REF SYM { $$ = f_generate_ref($1, $3); }
+
  | term '.' IP { $$ = f_new_inst(); $$->code = P('c','p'); $$->a1.p =
$1; $$->aux = T_IP; }
  | term '.' LEN { $$ = f_new_inst(); $$->code = 'L'; $$->a1.p = $1; }
  | term '.' MASK '(' term ')' { $$ = f_new_inst(); $$->code =
P('i','M'); $$->a1.p = $1; $$->a2.p = $5; }
@@ -877,6 +924,55 @@ cmd:
       $$->a1.p = $2;
       $$->a2.p = build_tree( $4 );
    }
+ | rtadot dynamic_attr REF SYM '=' term ';' {
+      struct f_inst_data *get_dyn;
+      struct f_inst *set_ref;
+      char **c;
+
+      $4 = cf_define_symbol_if_undef($4, SYM_REF, NULL);
+
+      get_dyn = f_new_inst2(sizeof(char *));
+      get_dyn->i = *($2);
+      get_dyn->i.code = P('e','a');
+      c = (char **) get_dyn->data;
+      *c = $4->name;
+
+      set_ref = f_new_inst();
+      set_ref->code = P('s','S');
+      set_ref->a1.p = get_dyn;
+      set_ref->a2.p = $6;
+
+      $$ = $2;
+      $$->code = P('e','S');
+      $$->a1.p = set_ref;
+   }
+ | SYM REF SYM '=' term ';' {
+      struct f_inst_data *get_sym;
+      struct f_inst *set_ref;
+      char **c;
+
+      if (($1->class & ~T_MASK) != SYM_VARIABLE)
+        cf_error( "You may set only variables." );
+
+      $3 = cf_define_symbol_if_undef($3, SYM_REF, NULL);
+
+      get_sym = f_new_inst2(sizeof(char *));
+      get_sym->i.code = 'V';
+      get_sym->i.a1.p = $1->def;
+      get_sym->i.a2.p = $1->name;
+      c = (char **) get_sym->data;
+      *c = $3->name;
+
+      set_ref = f_new_inst();
+      set_ref->code = P('s','S');
+      set_ref->a1.p = get_sym;
+      set_ref->a2.p = $5;
+
+      $$ = f_new_inst();
+      $$->code = 's';
+      $$->a1.p = $1;
+      $$->a2.p = set_ref;
+   }
    | rtadot dynamic_attr '.' EMPTY ';' { $$ = f_generate_empty($2); }
diff --git a/filter/f-util.c b/filter/f-util.c
index ad91002..a02d058 100644
--- a/filter/f-util.c
+++ b/filter/f-util.c
@@ -15,14 +15,21 @@
 struct f_inst *
 f_new_inst(void)
 {
-  struct f_inst * ret;
-  ret = cfg_alloc(sizeof(struct f_inst));
-  ret->code = ret->aux = 0;
-  ret->arg1 = ret->arg2 = ret->next = NULL;
+  struct f_inst *ret;
+  ret = cfg_allocz(sizeof(struct f_inst));
   ret->lineno = ifs->lino;
   return ret;
 }
 +struct f_inst_data *
+f_new_inst2(unsigned int size)
+{
+  struct f_inst_data *ret;
+  ret = cfg_allocz(sizeof(struct f_inst_data) + size);
+  ret->i.lineno = ifs->lino;
+  return ret;
+}
+
 struct f_inst *
 f_new_dynamic_attr(int type, int f_type, int code)
 {
@@ -71,6 +78,82 @@ f_generate_roa_check(struct symbol *sym, struct
f_inst *prefix, struct f_inst *a
   return &ret->i;
 }
 +struct f_inst *
+f_generate_struct(int type, ...)
+{
+  struct f_inst *rv;
+  va_list args;
+
+  va_start(args, type);
+
+  switch (type) {
+    case T_STRUCT_ASID:
+      {
+        struct f_inst *t1 = va_arg(args, struct f_inst *);
+        struct f_inst *t2 = va_arg(args, struct f_inst *);
+        int c1 = 0, c2 = 0;
+        struct f_asid ai = { 0, 0 };
+
+        if (t1->code == 'c') {
+          if (t1->aux != T_INT)
+            cf_error("Can't operate with non-integer value of AS part
in AS,ID constructor");
+
+          ai.as = t1->a2.i;
+          c1 = 1;
+        }
+
+        if (t2->code == 'c') {
+          if (t2->aux != T_QUAD)
+            cf_error("Can't operate with non-quad value of ID part in
AS,ID constructor");
+
+          ai.id = t2->a2.i;
+          c2 = 1;
+        }
+
+#ifndef IPV6
+        /* IP->Quad implicit conversion */
+        else if (t2->code == 'C') {
+          struct f_val *val = t2->a1.p;
+
+          if (val->type == T_QUAD)
+            ai.id = val->val.i;
+          else if (val->type == T_IP)
+            ai.id = ipa_to_u32(val->val.px.ip);
+          else
+            cf_error("Can't operate with non-quad value of ID part in
AS,ID constructor");
+
+          c2 = 1;
+        }
+#endif
+
+        if (c1 && c2) {
+          NEW_F_VAL;
+          rv = f_new_inst();
+          rv->code = 'C';
+          rv->a1.p = val;
+          val->type = T_STRUCT_ASID;
+          val->val.ai = ai;
+        } else {
+          rv = f_new_inst();
+          rv->code = P('m','a');
+          rv->a1.p = t1;
+          rv->a2.p = t2;
+        }
+      }
+      break;
+
+    default:
+      rv = NULL;
+  }
+
+  va_end(args);
+
+  if (!rv)
+    cf_error("Unknown structure type: %x", type);
+
+  return rv;
+}
+
 char *
 filter_name(struct filter *filter)
 {
diff --git a/filter/filter.c b/filter/filter.c
index 9b99f28..4b41d4f 100644
--- a/filter/filter.c
+++ b/filter/filter.c
@@ -50,6 +50,19 @@
  #define CMP_ERROR 999
 +/* [S]tructured types [E]rror codes and their text representation */
+#define SE_OK           0
+#define SE_NOT_STRUCT   1
+#define SE_IN_TYPE      2
+#define SE_NO_MEMBER    3
+
+static char *se_desc[] = {
+  [SE_OK]         = "",
+  [SE_NOT_STRUCT] = "Requested type is not structure",
+  [SE_IN_TYPE]    = "Attempt to set member to incompatible type",
+  [SE_NO_MEMBER]  = "Unknown member in structure",
+};
+
 static struct adata *
 adata_empty(struct linpool *pool, int l)
 {
@@ -188,6 +201,9 @@ val_compare(struct f_val v1, struct f_val v2)
     return uint_cmp(v1.val.i, v2.val.i);
   case T_EC:
     return u64_cmp(v1.val.ec, v2.val.ec);
+  case T_STRUCT_ASID:
+    return u64_cmp((((u64) v1.val.ai.as) << 32) | v1.val.ai.id,
+                   (((u64) v2.val.ai.as) << 32) | v2.val.ai.id);
   case T_IP:
     return ipa_compare(v1.val.px.ip, v2.val.px.ip);
   case T_PREFIX:
@@ -490,6 +506,7 @@ val_print(struct f_val v)
   case T_PAIR: logn("(%d,%d)", v.val.i >> 16, v.val.i & 0xffff); return;
   case T_QUAD: logn("%R", v.val.i); return;
   case T_EC: ec_format(buf2, v.val.ec); logn("%s", buf2); return;
+  case T_STRUCT_ASID: logn("{ %u, %R }", v.val.ai.as, v.val.ai.id); return;
   case T_PREFIX_SET: trie_print(v.val.ti); return;
   case T_SET: tree_print(v.val.t); return;
   case T_ENUM: logn("(enum %x)%d", v.type, v.val.i); return;
@@ -710,6 +727,102 @@ interpret(struct f_inst *what)
       break;
     }
 +  case P('m','a'):
+    {
+      TWOARGS;
+
+      struct f_asid ai;
+
+      if (v1.type != T_INT)
+        runtime("Can't operate with non-integer value of AS part in
AS,ID constructor");
+      ai.as = v1.val.i;
+
+      if (v2.type == T_QUAD)
+        ai.id = v2.val.i;
+#ifndef IPV6
+      /* IP->Quad implicit conversion */
+      else if (v2.type == T_IP)
+        ai.id = ipa_to_u32(v2.val.px.ip);
+#endif
+      else
+        runtime("Can't operate with non-quad value of ID part in AS,ID
constructor");
+
+      res.val.ai = ai;
+      res.type = T_STRUCT_ASID;
+      break;
+    }
+
+  case P('s','s'):
+    {
+      char *name;
+
+      ARG(v2, a2.p);
+
+      sym = what->a1.p;
+      name = sym->name;
+
+      i = SE_OK;
+      switch (v2.type) {
+      case T_STRUCT_ASID:
+       if (!strcmp(name, "ai.as")) {
+         res.type = T_INT;
+         res.val.i = v2.val.ai.as;
+       } else if (!strcmp(name, "ai.id")) {
+         res.type = T_QUAD;
+         res.val.i = v2.val.ai.id;
+       } else
+         i = SE_NO_MEMBER;
+       break;
+      default:
+       i = SE_NOT_STRUCT;
+      }
+
+      if (i != SE_OK)
+       runtime(se_desc[i]);
+    }
+    break;
+
+  case P('s','S'):
+    {
+      char **pname;
+      char *name;
+
+      TWOARGS;
+
+      pname = (char **) ((struct f_inst_data *) what->a1.p)->data;
+      name = *pname;
+
+      res = v1;
+      i = SE_OK;
+      switch (res.type) {
+      case T_STRUCT_ASID:
+       if (!strcmp(name, "ai.as")) {
+         if (v2.type == T_INT)
+           res.val.ai.as = v2.val.i;
+         else
+           i = SE_IN_TYPE;
+       } else if (!strcmp(name, "ai.id")) {
+         if (v2.type == T_QUAD)
+           res.val.ai.id = v2.val.i;
+#ifndef IPV6
+         /* IP->Quad implicit conversion */
+         else if (v2.type == T_IP)
+           res.val.ai.id = ipa_to_u32(v2.val.px.ip);
+#endif
+         else
+           i = SE_IN_TYPE;
+       } else
+         i = SE_NO_MEMBER;
+       break;
+      default:
+       i = SE_NOT_STRUCT;
+      }
+
+      if (i != SE_OK)
+        runtime(se_desc[i]);
+    }
+    break;
+
 /* Relational operators */
  #define COMPARE(f,x) \
@@ -937,6 +1050,12 @@ interpret(struct f_inst *what)
          res.val.px.ip = * (ip_addr *) ad->data;
        }
        break;
+      case T_STRUCT_ASID:
+        {
+         struct adata * ad = e->u.ptr;
+         res.val.ai = * (struct f_asid *) ad->data;
+        }
+        break;
       case T_PATH:
       case T_CLIST:
       case T_ECLIST:
@@ -998,6 +1117,14 @@ interpret(struct f_inst *what)
          l->attrs[0].u.ptr = ad;
        }
        break;
+      case T_STRUCT_ASID:
+       {
+         struct adata *ad = lp_alloc(f_pool, sizeof(struct adata) +
sizeof(struct f_asid));
+         ad->length = sizeof(struct f_asid);
+         (* (struct f_asid *) ad->data) = v1.val.ai;
+         l->attrs[0].u.ptr = ad;
+       }
+       break;
       case T_PATH:
       case T_CLIST:
       case T_ECLIST:
@@ -1352,6 +1479,7 @@ i_same(struct f_inst *f1, struct f_inst *f2)
   case '&':
   case P('m','p'):
   case P('m','c'):
+  case P('m','a'):
   case P('!','='):
   case P('=','='):
   case '<':
@@ -1361,6 +1489,7 @@ i_same(struct f_inst *f1, struct f_inst *f2)
   case '~': TWOARGS; break;
   case P('d','e'): ONEARG; break;
 +  case P('s','s'):
   case 's':
     ARG(v2, a2.p);
     {
@@ -1374,6 +1503,23 @@ i_same(struct f_inst *f1, struct f_inst *f2)
     }
     break;
 +  case P('s','S'):
+    TWOARGS;
+    {
+      char **pname1, **pname2;
+      char *name1, *name2;
+
+      pname1 = (char **) ((struct f_inst_data *) f1->a1.p)->data;
+      name1 = *pname1;
+
+      pname2 = (char **) ((struct f_inst_data *) f2->a1.p)->data;
+      name2 = *pname2;
+
+      if (strcmp(name1, name2))
+       return 0;
+    }
+    break;
+
   case 'c':      switch (f1->aux) {
 diff --git a/filter/filter.h b/filter/filter.h
index aed4418..970d093 100644
--- a/filter/filter.h
+++ b/filter/filter.h
@@ -29,6 +29,11 @@ struct f_inst {              /* Instruction */
   int lineno;
 };
 +struct f_inst_data {
+  struct f_inst i;
+  byte data[0];
+};
+
 #define arg1 a1.p
 #define arg2 a2.p
 @@ -48,11 +53,17 @@ struct f_prefix {
   /* If range then prefix must be in range (len >> 16 & 0xff, len >> 8
& 0xff) */
 };
 +struct f_asid {
+  u32 as;
+  u32 id;
+};
+
 struct f_val {
   int type;
   union {
     int i;
     u64 ec;
+    struct f_asid ai;
     /*    ip_addr ip; Folded into prefix */    
     struct f_prefix px;
     char *s;
@@ -69,10 +80,12 @@ struct filter {
 };
  struct f_inst *f_new_inst(void);
+struct f_inst_data *f_new_inst2(unsigned int size);
 struct f_inst *f_new_dynamic_attr(int type, int f_type, int code);     /*
Type as core knows it, type as filters know it, and code of dynamic
attribute */
 struct f_tree *f_new_tree(void);
 struct f_inst *f_generate_complex(int operation, int operation_aux,
struct f_inst *dyn, struct f_inst *argument);
 struct f_inst *f_generate_roa_check(struct symbol *sym, struct f_inst
*prefix, struct f_inst *asn);
+struct f_inst *f_generate_struct(int type, ...);
   struct f_tree *build_tree(struct f_tree *);
@@ -177,9 +190,21 @@ void val_print(struct f_val v);
 #define T_ECLIST 0x26          /* Extended community list */
 #define T_EC 0x27              /* Extended community value, u64 */
 -#define T_RETURN 0x40
-#define T_SET 0x80
-#define T_PREFIX_SET 0x81
+/* Put set types in 0x40..0x4f range */
+#define T_SET 0x40             /* General set type */
+#define T_PREFIX_SET 0x41      /* Special set for prefixes */
+/* new sets go here */
+
+/* Put structured types in 0x50..0x5f range */
+#define T_STRUCT_LO 0x50
+#define T_STRUCT_HI 0x5f
+
+#define T_STRUCT_ASID 0x50     /* AS,ID structure (BGP_AGGREGATOR) */
+/* new structs go here */
+
+#define T_STRUCT T_STRUCT_LO ... T_STRUCT_HI
+
+#define T_RETURN 0x80
   #define SA_FROM               1    diff --git a/nest/route.h b/nest/route.h
index 35b5fa1..7aadfc9 100644
--- a/nest/route.h
+++ b/nest/route.h
@@ -392,6 +392,7 @@ typedef struct eattr {
 #define EAF_TYPE_IP_ADDRESS 0x04       /* IP address */
 #define EAF_TYPE_ROUTER_ID 0x05                /* Router ID (IPv4 address) */
 #define EAF_TYPE_AS_PATH 0x06          /* BGP AS path (encoding per RFC
1771:4.3) */
+#define EAF_TYPE_BGP_AGGREGATOR 0x08   /* BGP AGGREGATOR attribute */
 #define EAF_TYPE_INT_SET 0x0a          /* Set of u32's (e.g., a community 
list) */
 #define EAF_TYPE_EC_SET 0x0e           /* Set of pairs of u32's - ext. 
community
list */
 #define EAF_TYPE_UNDEF 0x0f            /* `force undefined' entry */
diff --git a/proto/bgp/attrs.c b/proto/bgp/attrs.c
index c27a498..706ed7d 100644
--- a/proto/bgp/attrs.c
+++ b/proto/bgp/attrs.c
@@ -231,14 +231,10 @@ bgp_check_aggregator(struct bgp_proto *p, byte *a
UNUSED, int len)
 static void
 bgp_format_aggregator(eattr *a, byte *buf, int buflen UNUSED)
 {
-  struct adata *ad =  a->u.ptr;
-  byte *data = ad->data;
-  u32 as;
-
-  as = get_u32(data);
-  data += 4;
+  struct adata *ad = a->u.ptr;
+  struct f_asid *ai = (struct f_asid *) ad->data;
 -  bsprintf(buf, "%d.%d.%d.%d AS%u", data[0], data[1], data[2],
data[3], as);
+  bsprintf(buf, "%R AS%u", ai->id, ai->as);
 }
  static int
@@ -302,7 +298,7 @@ static struct attr_desc bgp_attr_table[] = {
     NULL, NULL },
   { "atomic_aggr", 0, BAF_TRANSITIVE, EAF_TYPE_OPAQUE, 1,                      
/*
BA_ATOMIC_AGGR */
     NULL, NULL },
-  { "aggregator", -1, BAF_OPTIONAL | BAF_TRANSITIVE, EAF_TYPE_OPAQUE,
1,      /* BA_AGGREGATOR */
+  { "aggregator", -1, BAF_OPTIONAL | BAF_TRANSITIVE,
EAF_TYPE_BGP_AGGREGATOR, 1,/* BA_AGGREGATOR */
     bgp_check_aggregator, bgp_format_aggregator },
   { "community", -1, BAF_OPTIONAL | BAF_TRANSITIVE, EAF_TYPE_INT_SET,
1,      /* BA_COMMUNITY */
     bgp_check_community, NULL },
@@ -597,6 +593,7 @@ bgp_encode_attrs(struct bgp_proto *p, byte *w,
ea_list *attrs, int remains)
            memcpy(w, &ip, len);
            break;
          }
+       case EAF_TYPE_BGP_AGGREGATOR:
        case EAF_TYPE_INT_SET:
        case EAF_TYPE_EC_SET:
          {
@@ -1660,6 +1657,7 @@ bgp_decode_attrs(struct bgp_conn *conn, byte
*attr, unsigned int len, struct lin
        case EAF_TYPE_IP_ADDRESS:
          ipa_ntoh(*(ip_addr *)ad->data);
          break;
+       case EAF_TYPE_BGP_AGGREGATOR:
        case EAF_TYPE_INT_SET:
        case EAF_TYPE_EC_SET:
          {
diff --git a/proto/bgp/config.Y b/proto/bgp/config.Y
index 19f7a21..0c949ff 100644
--- a/proto/bgp/config.Y
+++ b/proto/bgp/config.Y
@@ -125,7 +125,7 @@ CF_ADDTO(dynamic_attr, BGP_LOCAL_PREF
 CF_ADDTO(dynamic_attr, BGP_ATOMIC_AGGR
        { $$ = f_new_dynamic_attr(EAF_TYPE_OPAQUE, T_EMPTY, EA_CODE(EAP_BGP,
BA_ATOMIC_AGGR)); })
 CF_ADDTO(dynamic_attr, BGP_AGGREGATOR
-       { $$ = f_new_dynamic_attr(EAF_TYPE_OPAQUE, T_EMPTY, EA_CODE(EAP_BGP,
BA_AGGREGATOR)); })
+       { $$ = f_new_dynamic_attr(EAF_TYPE_BGP_AGGREGATOR, T_STRUCT_ASID,
EA_CODE(EAP_BGP, BA_AGGREGATOR)); })
 CF_ADDTO(dynamic_attr, BGP_COMMUNITY
        { $$ = f_new_dynamic_attr(EAF_TYPE_INT_SET, T_CLIST, EA_CODE(EAP_BGP,
BA_COMMUNITY)); })
 CF_ADDTO(dynamic_attr, BGP_ORIGINATOR_ID
-- 
1.7.10.4



Reply via email to