]> git.ipfire.org Git - thirdparty/bird.git/commitdiff
Explicit definition structures of route attributes
authorMaria Matejka <mq@ucw.cz>
Sat, 19 Mar 2022 15:23:42 +0000 (16:23 +0100)
committerMaria Matejka <mq@ucw.cz>
Wed, 4 May 2022 13:39:19 +0000 (15:39 +0200)
Changes in internal API:

* Every route attribute must be defined as struct ea_class somewhere.
* Registration of route attributes known at startup must be done by
  ea_register_init() from protocol build functions.
* Every attribute has now its symbol registered in a global symbol table
  defined as SYM_ATTRIBUTE
* All attribute ID's are dynamically allocated.
* Attribute value custom formatting hook is defined in the ea_class.
* Attribute names are the same for display and filters, always prefixed
  by protocol name.

Also added some unit testing code for filters with route attributes.

54 files changed:
conf/cf-lex.l
conf/conf.h
conf/confbase.Y
filter/config.Y
filter/data.h
filter/decl.m4
filter/f-inst.c
filter/f-inst.h
filter/f-util.c
filter/filter.h
filter/test.conf
lib/event_test.c
lib/mempool.c
lib/resource.c
lib/resource.h
lib/route.h
nest/config.Y
nest/proto.c
nest/protocol.h
nest/rt-attr.c
nest/rt-dev.c
nest/rt-table.c
proto/babel/babel.c
proto/babel/babel.h
proto/babel/config.Y
proto/bfd/bfd.c
proto/bgp/attrs.c
proto/bgp/bgp.c
proto/bgp/bgp.h
proto/bgp/config.Y
proto/mrt/mrt.c
proto/ospf/config.Y
proto/ospf/ospf.c
proto/ospf/ospf.h
proto/ospf/rt.c
proto/ospf/topology.c
proto/perf/perf.c
proto/pipe/pipe.c
proto/radv/config.Y
proto/radv/radv.c
proto/radv/radv.h
proto/rip/config.Y
proto/rip/rip.c
proto/rip/rip.h
proto/rpki/rpki.c
proto/static/static.c
sysdep/linux/krt-sys.h
sysdep/linux/netlink.Y
sysdep/linux/netlink.c
sysdep/unix/krt.Y
sysdep/unix/krt.c
sysdep/unix/krt.h
sysdep/unix/main.c
test/bt-utils.c

index bd424c69be492c10efcc8e60140b5883a097f140..e84e1d9dae40bee40be9c2cf35ec6a678ad0c60c 100644 (file)
@@ -86,9 +86,12 @@ static uint cf_hash(const byte *c);
 HASH_DEFINE_REHASH_FN(SYM, struct symbol)
 
 HASH(struct keyword) kw_hash;
+HASH(struct ea_class) ea_name_hash;
 
 struct sym_scope *conf_this_scope;
-struct sym_scope *global_root_scope;
+
+static struct sym_scope global_root_scope__init = { .active = 1, };
+struct sym_scope *global_root_scope = &global_root_scope__init;
 
 linpool *cfg_mem;
 
@@ -598,6 +601,25 @@ cf_new_symbol(const byte *c)
   return s;
 }
 
+static struct symbol *
+cf_root_symbol(const byte *c)
+{
+  uint l = strlen(c);
+  if (l > SYM_MAX_LEN)
+    bug("Root symbol %s too long", c);
+
+  struct symbol *s = mb_alloc(&root_pool, sizeof(struct symbol) + l + 1);
+  *s = (struct symbol) { .scope = global_root_scope, .class = SYM_VOID, };
+  memcpy(s->name, c, l+1);
+
+  if (!global_root_scope->hash.data)
+    HASH_INIT(global_root_scope->hash, &root_pool, SYM_ORDER);
+
+  HASH_INSERT2(global_root_scope->hash, SYM, &root_pool, s);
+  return s;
+}
+
+
 /**
  * cf_find_symbol_scope - find a symbol by name
  * @scope: config scope
@@ -652,7 +674,7 @@ cf_localize_symbol(struct symbol *sym)
   /* If the symbol type is void, it has been recently allocated just in this scope. */
   if (!sym->class)
     return sym;
-  
+
   /* If the scope is the current, it is already defined in this scope. */
   if (sym->scope == conf_this_scope)
     cf_error("Symbol already defined");
@@ -716,8 +738,34 @@ cf_lex_init_kh(void)
   struct keyword *k;
   for (k=keyword_list; k->name; k++)
     HASH_INSERT(kw_hash, KW, k);
+}
+
+void
+ea_lex_register(struct ea_class *def)
+{
+  struct symbol *sym = cf_root_symbol(def->name);
+  sym->class = SYM_ATTRIBUTE;
+  sym->attribute = def;
+  def->sym = sym;
+}
 
-  global_root_scope = mb_allocz(&root_pool, sizeof(*global_root_scope));
+void
+ea_lex_unregister(struct ea_class *def)
+{
+  struct symbol *sym = def->sym;
+  HASH_REMOVE2(global_root_scope->hash, SYM, &root_pool, sym);
+  mb_free(sym);
+  def->sym = NULL;
+}
+
+struct ea_class *
+ea_class_find_by_name(const char *name)
+{
+  struct symbol *sym = cf_find_symbol(global_root_scope, name);
+  if (!sym || (sym->class != SYM_ATTRIBUTE))
+    return NULL;
+  else
+    return sym->attribute;
 }
 
 /**
index 2700295b112f2fcb528f0e2d57f0c34a33122d28..18de8def0352e6a8d18fe25bacb7d9346cad6a79 100644 (file)
@@ -120,7 +120,7 @@ struct symbol {
     const struct f_line *function;     /* For SYM_FUNCTION */
     const struct filter *filter;       /* For SYM_FILTER */
     struct rtable_config *table;       /* For SYM_TABLE */
-    struct f_dynamic_attr *attribute;  /* For SYM_ATTRIBUTE */
+    struct ea_class *attribute;                /* For SYM_ATTRIBUTE */
     struct f_val *val;                 /* For SYM_CONSTANT */
     uint offset;                       /* For SYM_VARIABLE */
   };
index a81560dc4faa3678a58c4aec61eb171194dbe6db..2286b2576f4adfb769e02e59b001834d6ad00a99 100644 (file)
@@ -71,8 +71,9 @@ CF_DECLS
   } xp;
   enum filter_return fret;
   enum ec_subtype ecs;
-  struct f_dynamic_attr fda;
+  struct ea_class *ea_class;
   struct f_static_attr fsa;
+  struct f_attr_bit fab;
   struct f_lval flv;
   struct f_line *fl;
   const struct filter *f;
index 92656f7c47f31221d5de67321c96bac3a4bc9ab2..f21f1c8e83100b011b7c09fcfad332f9a99e2397 100644 (file)
@@ -22,6 +22,13 @@ static inline u32 pair_b(u32 p) { return p & 0xFFFF; }
 #define f_generate_complex(fi_code, da, arg) \
   f_new_inst(FI_EA_SET, f_new_inst(fi_code, f_new_inst(FI_EA_GET, da), arg), da)
 
+#define f_generate_complex_sym(fi_code, sym, arg) ({ \
+  if (sym->class != SYM_ATTRIBUTE) \
+    cf_error("Can't empty %s: not an attribute", sym->name); \
+  f_generate_complex(fi_code, sym->attribute, arg); \
+})
+  
+
 /*
  * Sets and their items are during parsing handled as lists, linked
  * through left ptr. The first item in a list also contains a pointer
@@ -161,27 +168,31 @@ f_new_lc_item(u32 f1, u32 t1, u32 f2, u32 t2, u32 f3, u32 t3)
 }
 
 static inline struct f_inst *
-f_generate_empty(struct f_dynamic_attr dyn)
+f_generate_empty(const struct symbol *sym)
 {
-  const struct f_val *empty = f_get_empty(dyn.type);
+  if (sym->class != SYM_ATTRIBUTE)
+    cf_error("Can't empty %s: not an attribute", sym->name);
+
+  const struct ea_class *def = sym->attribute;
+  const struct f_val *empty = f_get_empty(def->type);
   if (!empty)
-    cf_error("Can't empty that attribute");
+    cf_error("Can't empty attribute %s", def->name);
 
-  return f_new_inst(FI_EA_SET, f_new_inst(FI_CONSTANT, *empty), dyn);
+  return f_new_inst(FI_EA_SET, f_new_inst(FI_CONSTANT, *empty), def);
 }
 
-#define BA_AS_PATH 0x02
-
 static inline struct f_inst *
 f_implicit_roa_check(struct rtable_config *tab)
 {
-  struct f_dynamic_attr fda = f_new_dynamic_attr(T_PATH, EA_CODE(PROTOCOL_BGP, BA_AS_PATH));
+  const struct ea_class *def = ea_class_find("bgp_path");
+  if (!def)
+    cf_error("Fatal: Couldn't find BGP path attribute definition.");
 
   struct f_static_attr fsa = f_new_static_attr(T_NET, SA_NET, 1);
 
   return f_new_inst(FI_ROA_CHECK,
            f_new_inst(FI_RTA_GET, fsa),
-           f_new_inst(FI_AS_PATH_LAST, f_new_inst(FI_EA_GET, fda)),
+           f_new_inst(FI_AS_PATH_LAST, f_new_inst(FI_EA_GET, def)),
            tab);
 }
 
@@ -293,8 +304,8 @@ CF_KEYWORDS(FUNCTION, PRINT, PRINTN, UNSET, RETURN,
 
 %type <xp> cmds_int cmd_prep
 %type <x> term block cmd cmds constant constructor print_list var_list function_call symbol_value bgp_path_expr bgp_path bgp_path_tail
-%type <fda> dynamic_attr attr_bit
 %type <fsa> static_attr
+%type <fab> attr_bit
 %type <f> filter where_filter
 %type <fl> filter_body function_body
 %type <flv> lvalue
@@ -335,7 +346,14 @@ filter_eval:
 
 conf: custom_attr ;
 custom_attr: ATTRIBUTE type symbol ';' {
-  cf_define_symbol($3, SYM_ATTRIBUTE, attribute, ca_lookup(new_config->pool, $3->name, $2)->fda);
+  if (($3->class == SYM_ATTRIBUTE) && ($3->scope == new_config->root_scope))
+    cf_error("Duplicate attribute %s definition", $3->name);
+
+  cf_define_symbol($3, SYM_ATTRIBUTE, attribute,
+      ea_register_alloc(new_config->pool, (struct ea_class) {
+       .name = $3->name,
+       .type = $2,
+       })->class);
 };
 
 conf: bt_test_suite ;
@@ -736,7 +754,7 @@ symbol_value: CF_SYM_KNOWN
        $$ = f_new_inst(FI_VAR_GET, $1);
        break;
       case SYM_ATTRIBUTE:
-       $$ = f_new_inst(FI_EA_GET, *$1->attribute);
+       $$ = f_new_inst(FI_EA_GET, $1->attribute);
        break;
       default:
        cf_error("Can't get value of symbol %s", $1->name);
@@ -785,11 +803,9 @@ term:
  | constructor { $$ = $1; }
 
  | static_attr { $$ = f_new_inst(FI_RTA_GET, $1); }
-
- | dynamic_attr { $$ = f_new_inst(FI_EA_GET, $1); }
  | attr_bit {
     struct f_inst *c = f_new_inst(FI_CONSTANT, (struct f_val) { .type = T_INT, .val.i = (1U << $1.bit)});
-    $$ = f_new_inst(FI_EQ, c, f_new_inst(FI_BITAND, f_new_inst(FI_EA_GET, $1), c));
+    $$ = f_new_inst(FI_EQ, c, f_new_inst(FI_BITAND, f_new_inst(FI_EA_GET, $1.class), c));
   }
 
  | term '.' IS_V4 { $$ = f_new_inst(FI_IS_V4, $1); }
@@ -833,8 +849,6 @@ term:
 
  | FORMAT '(' term ')' {  $$ = f_new_inst(FI_FORMAT, $3); }
 
-/* | term '.' LEN { $$->code = P('P','l'); } */
-
  | function_call
  ;
 
@@ -867,7 +881,9 @@ cmd:
         $$ = f_new_inst(FI_VAR_SET, $3, $1);
         break;
        case SYM_ATTRIBUTE:
-        $$ = f_new_inst(FI_EA_SET, $3, *$1->attribute);
+        if ($1->attribute->readonly)
+          cf_error("Attribute %s is read-only", $1->attribute->name);
+        $$ = f_new_inst(FI_EA_SET, $3, $1->attribute);
         break;
        default:
         cf_error("Can't assign to symbol %s", $1->name);
@@ -877,22 +893,23 @@ cmd:
      DBG( "Ook, we'll return the value\n" );
      $$ = f_new_inst(FI_RETURN, $2);
    }
- | dynamic_attr '=' term ';' {
-     $$ = f_new_inst(FI_EA_SET, $3, $1);
-   }
  | static_attr '=' term ';' {
      if ($1.readonly)
        cf_error( "This static attribute is read-only.");
      $$ = f_new_inst(FI_RTA_SET, $3, $1);
    }
- | UNSET '(' dynamic_attr ')' ';' {
-     $$ = f_new_inst(FI_EA_UNSET, $3);
+ | UNSET '(' CF_SYM_KNOWN ')' ';' {
+     if ($3->class != SYM_ATTRIBUTE)
+       cf_error("Can't unset %s", $3->name);
+     if ($3->attribute->readonly)
+       cf_error("Attribute %s is read-only", $3->attribute->name);
+     $$ = f_new_inst(FI_EA_UNSET, $3->attribute);
    }
  | attr_bit '=' term ';' {
      $$ = f_new_inst(FI_CONDITION, $3,
-       f_generate_complex(FI_BITOR, $1,
+       f_generate_complex(FI_BITOR, $1.class,
              f_new_inst(FI_CONSTANT, (struct f_val) { .type = T_INT, .val.i = (1U << $1.bit)})),
-       f_generate_complex(FI_BITAND, $1,
+       f_generate_complex(FI_BITAND, $1.class,
              f_new_inst(FI_CONSTANT, (struct f_val) { .type = T_INT, .val.i = ~(1U << $1.bit)}))
      );
    }
@@ -919,11 +936,11 @@ cmd:
       $$ = f_new_inst(FI_SWITCH, $2, build_tree($4));
    }
 
- | dynamic_attr '.' EMPTY ';' { $$ = f_generate_empty($1); }
- | dynamic_attr '.' PREPEND '(' term ')' ';'   { $$ = f_generate_complex( FI_PATH_PREPEND, $1, $5 ); }
- | dynamic_attr '.' ADD '(' term ')' ';'       { $$ = f_generate_complex( FI_CLIST_ADD, $1, $5 ); }
- | dynamic_attr '.' DELETE '(' term ')' ';'    { $$ = f_generate_complex( FI_CLIST_DEL, $1, $5 ); }
- | dynamic_attr '.' FILTER '(' term ')' ';'    { $$ = f_generate_complex( FI_CLIST_FILTER, $1, $5 ); }
+ | CF_SYM_KNOWN '.' EMPTY ';' { $$ = f_generate_empty($1); }
+ | CF_SYM_KNOWN '.' PREPEND '(' term ')' ';'   { $$ = f_generate_complex_sym( FI_PATH_PREPEND, $1, $5 ); }
+ | CF_SYM_KNOWN '.' ADD '(' term ')' ';'       { $$ = f_generate_complex_sym( FI_CLIST_ADD, $1, $5 ); }
+ | CF_SYM_KNOWN '.' DELETE '(' term ')' ';'    { $$ = f_generate_complex_sym( FI_CLIST_DEL, $1, $5 ); }
+ | CF_SYM_KNOWN '.' FILTER '(' term ')' ';'    { $$ = f_generate_complex_sym( FI_CLIST_FILTER, $1, $5 ); }
  | BT_ASSERT '(' get_cf_position term get_cf_position ')' ';' { $$ = assert_done($4, $3 + 1, $5 - 1); }
  | BT_CHECK_ASSIGN '(' get_cf_position lvalue get_cf_position ',' term ')' ';' { $$ = assert_assign(&$4, $7, $3 + 1, $5 - 1); }
  ;
@@ -934,8 +951,17 @@ get_cf_position:
 };
 
 lvalue:
-   CF_SYM_KNOWN { cf_assert_symbol($1, SYM_VARIABLE); $$ = (struct f_lval) { .type = F_LVAL_VARIABLE, .sym = $1 }; }
+   CF_SYM_KNOWN {
+      switch ($1->class) {
+       case SYM_VARIABLE_RANGE:
+         $$ = (struct f_lval) { .type = F_LVAL_VARIABLE, .sym = $1 };
+         break;
+       case SYM_ATTRIBUTE:
+         $$ = (struct f_lval) { .type = F_LVAL_EA, .da = $1->attribute };
+         break;
+      }
+  }
  | static_attr { $$ = (struct f_lval) { .type = F_LVAL_SA, .sa = $1 }; }
| dynamic_attr { $$ = (struct f_lval) { .type = F_LVAL_EA, .da = $1 }; };
+ ;
 
 CF_END
index cba47d6ac5c12ee4b290edcc4667b233a5d0dc50..4d2622ab795ffdaa8f25b8baef56ee60f82834ba 100644 (file)
@@ -21,13 +21,6 @@ struct f_val {
 
 #define fputip(a)   ({ ip_addr *ax = falloc(sizeof(*ax)); *ax = (a); ax; })
 
-/* Dynamic attribute definition (eattrs) */
-struct f_dynamic_attr {
-  btype type;          /* EA type (EAF_*) */
-  u8 bit;              /* For bitfield accessors */
-  uint ea_code;                /* EA code */
-};
-
 enum f_sa_code {
   SA_FROM = 1,
   SA_GW,
@@ -53,7 +46,6 @@ struct f_static_attr {
 /* Filter l-value type */
 enum f_lval_type {
   F_LVAL_VARIABLE,
-  F_LVAL_PREFERENCE,
   F_LVAL_SA,
   F_LVAL_EA,
 };
@@ -63,7 +55,7 @@ struct f_lval {
   enum f_lval_type type;
   union {
     struct symbol *sym;
-    struct f_dynamic_attr da;
+    const struct ea_class *da;
     struct f_static_attr sa;
   };
 };
index 2e4fb2358b5a8989e68ee12ce54ad05cd725d5b5..c59cd7f3b2258532855c1d1f7b0c438f69057795 100644 (file)
@@ -94,7 +94,7 @@ FID_DUMP_BODY()m4_dnl
 debug("%s" $4 "\n", INDENT, $5);
 ]])
 FID_INTERPRET_EXEC()m4_dnl
-const $1 $2 = whati->$2
+$1 $2 = whati->$2
 FID_INTERPRET_BODY')
 
 #      Instruction arguments are needed only until linearization is done.
@@ -256,7 +256,7 @@ FID_INTERPRET_BODY()')
 m4_define(SYMBOL, `FID_MEMBER(struct symbol *, sym, [[strcmp(f1->sym->name, f2->sym->name) || (f1->sym->class != f2->sym->class)]], "symbol %s", item->sym->name)')
 m4_define(RTC, `FID_MEMBER(struct rtable_config *, rtc, [[strcmp(f1->rtc->name, f2->rtc->name)]], "route table %s", item->rtc->name)')
 m4_define(STATIC_ATTR, `FID_MEMBER(struct f_static_attr, sa, f1->sa.sa_code != f2->sa.sa_code,,)')
-m4_define(DYNAMIC_ATTR, `FID_MEMBER(struct f_dynamic_attr, da, f1->da.ea_code != f2->da.ea_code,,)')
+m4_define(DYNAMIC_ATTR, `FID_MEMBER(const struct ea_class *, da, f1->da != f2->da,,)')
 m4_define(ACCESS_RTE, `FID_HIC(,[[do { if (!fs->rte) runtime("No route to access"); } while (0)]],NEVER_CONSTANT())')
 
 #      2) Code wrapping
index 7b34ef9127975b492b688831e3ddb7114ccba23f..c5947955494fdf2e9c535a1da2fd3710038e3a13 100644 (file)
     DYNAMIC_ATTR;
     ACCESS_RTE;
     ACCESS_EATTRS;
-    RESULT_TYPE(da.type);
+    RESULT_TYPE(da->type);
     {
       const struct f_val *empty;
-      eattr *e = ea_find(*fs->eattrs, da.ea_code);
+      const eattr *e = ea_find(*fs->eattrs, da->id);
 
       if (e)
       {
-       ASSERT_DIE(e->type == da.type);
+       ASSERT_DIE(e->type == da->type);
 
        switch (e->type) {
          case T_IP:
                }]]);
        }
       }
-      else if (empty = f_get_empty(da.type))
+      else if (empty = f_get_empty(da->type))
        RESULT_VAL(*empty);
       else
        RESULT_VOID;
     ACCESS_EATTRS;
     ARG_ANY(1);
     DYNAMIC_ATTR;
-    ARG_TYPE(1, da.type);
+    ARG_TYPE(1, da->type);
     {
       struct eattr *a;
 
-      if (da.type >= EAF_TYPE__MAX)
+      if (da->type >= EAF_TYPE__MAX)
        bug("Unsupported attribute type");
 
       f_rta_cow(fs);
 
-      switch (da.type) {
+      switch (da->type) {
       case T_OPAQUE:
       case T_IFACE:
        runtime( "Setting opaque attribute is not allowed" );
 
       case T_IP:
        a = ea_set_attr(fs->eattrs,
-           EA_LITERAL_STORE_ADATA(da.ea_code, da.type, 0, &v1.val.ip, sizeof(ip_addr)));
+           EA_LITERAL_STORE_ADATA(da, 0, &v1.val.ip, sizeof(ip_addr)));
        break;
 
       default:
        a = ea_set_attr(fs->eattrs,
-           EA_LITERAL_GENERIC(da.ea_code, da.type, 0, .u = v1.val.bval));
+           EA_LITERAL_GENERIC(da->id, da->type, 0, .u = v1.val.bval));
        break;
       }
 
     ACCESS_EATTRS;
 
     f_rta_cow(fs);
-    ea_unset_attr(fs->eattrs, 1, da.ea_code);
+    ea_unset_attr(fs->eattrs, 1, da);
   }
 
   INST(FI_LENGTH, 1, 1) {      /* Get length of */
index 32da4653467f09bba55a96d9036078e0666b010d..047a66c93dc50090a5aa0ebbe2b666f3bc9b1904 100644 (file)
@@ -87,15 +87,17 @@ void f_add_lines(const struct f_line_item *what, struct filter_iterator *fit);
 
 
 struct filter *f_new_where(struct f_inst *);
-static inline struct f_dynamic_attr f_new_dynamic_attr(u8 type, uint code)
-{ return (struct f_dynamic_attr) { .type = type, .ea_code = code }; }
-static inline struct f_dynamic_attr f_new_dynamic_attr_bit(u8 bit, uint code)
-{ return (struct f_dynamic_attr) { .type = T_INT, .bit = bit, .ea_code = code }; }
 static inline struct f_static_attr f_new_static_attr(btype type, int code, int readonly)
 { return (struct f_static_attr) { .type = type, .sa_code = code, .readonly = readonly }; }
-struct f_inst *f_generate_complex(enum f_instruction_code fi_code, struct f_dynamic_attr da, struct f_inst *argument);
 struct f_inst *f_generate_roa_check(struct rtable_config *table, struct f_inst *prefix, struct f_inst *asn);
 
+struct f_attr_bit {
+  const struct ea_class *class;
+  uint bit;
+};
+
+#define f_new_dynamic_attr_bit(_bit, _name)  ((struct f_attr_bit) { .bit = _bit, .class = ea_class_find(_name) })
+
 /* Hook for call bt_assert() function in configuration */
 extern void (*bt_assert_hook)(int result, const struct f_line_item *assert);
 
index 83ae01f6d84d0fd6af8572da88a01f53e928f419..fb93ee80c62fe4ee8c4dd24be29219167bac2424 100644 (file)
@@ -2,7 +2,7 @@
  *     Filters: utility functions
  *
  *     Copyright 1998 Pavel Machek <pavel@ucw.cz>
- *               2017 Jan Maria Matejka <mq@ucw.cz>
+ *               2017 Maria Matejka <mq@ucw.cz>
  *
  *     Can be freely distributed and used under the terms of the GNU GPL.
  */
@@ -40,129 +40,3 @@ struct filter *f_new_where(struct f_inst *where)
   f->root = f_linearize(cond);
   return f;
 }
-
-#define CA_KEY(n)      n->name, n->fda.type
-#define CA_NEXT(n)     n->next
-#define CA_EQ(na,ta,nb,tb)     (!strcmp(na,nb) && (ta == tb))
-#define CA_FN(n,t)     (mem_hash(n, strlen(n)) ^ (t*0xaae99453U))
-#define CA_ORDER       8 /* Fixed */
-
-struct ca_storage {
-  struct ca_storage *next;
-  struct f_dynamic_attr fda;
-  u32 uc;
-  char name[0];
-};
-
-HASH(struct ca_storage) ca_hash;
-
-static struct idm ca_idm;
-static struct ca_storage **ca_storage;
-static uint ca_storage_max;
-
-static void
-ca_free(resource *r)
-{
-  struct custom_attribute *ca = (void *) r;
-  struct ca_storage *cas = HASH_FIND(ca_hash, CA, ca->name, ca->fda->type);
-  ASSERT(cas);
-
-  ca->name = NULL;
-  ca->fda = NULL;
-  if (!--cas->uc) {
-    uint id = EA_CUSTOM_ID(cas->fda.ea_code);
-    idm_free(&ca_idm, id);
-    HASH_REMOVE(ca_hash, CA, cas);
-    ca_storage[id] = NULL;
-    mb_free(cas);
-  }
-}
-
-static void
-ca_dump(resource *r)
-{
-  struct custom_attribute *ca = (void *) r;
-  debug("name \"%s\" id 0x%04x ea_type 0x%02x\n",
-      ca->name, ca->fda->ea_code, ca->fda->type);
-}
-
-static struct resclass ca_class = {
-  .name = "Custom attribute",
-  .size = sizeof(struct custom_attribute),
-  .free = ca_free,
-  .dump = ca_dump,
-  .lookup = NULL,
-  .memsize = NULL,
-};
-
-struct custom_attribute *
-ca_lookup(pool *p, const char *name, btype type)
-{
-  switch (type) {
-    case T_INT:
-    case T_IP:
-    case T_QUAD:
-    case T_PATH:
-    case T_CLIST:
-    case T_ECLIST:
-    case T_LCLIST:
-      break;
-    default:
-      cf_error("Custom route attribute of unsupported type");
-  }
-
-  static int inited = 0;
-  if (!inited) {
-    idm_init(&ca_idm, &root_pool, 8);
-    HASH_INIT(ca_hash, &root_pool, CA_ORDER);
-
-    ca_storage_max = 256;
-    ca_storage = mb_allocz(&root_pool, sizeof(struct ca_storage *) * ca_storage_max);
-
-    inited++;
-  }
-
-  struct ca_storage *cas = HASH_FIND(ca_hash, CA, name, type);
-  if (cas) {
-    cas->uc++;
-  } else {
-
-    uint id = idm_alloc(&ca_idm);
-
-    if (id >= EA_CUSTOM_BIT)
-      cf_error("Too many custom attributes.");
-
-    if (id >= ca_storage_max) {
-      ca_storage_max *= 2;
-      ca_storage = mb_realloc(ca_storage, sizeof(struct ca_storage *) * ca_storage_max * 2);
-    }
-
-    cas = mb_allocz(&root_pool, sizeof(struct ca_storage) + strlen(name) + 1);
-    cas->fda = f_new_dynamic_attr(type, EA_CUSTOM(id));
-    cas->uc = 1;
-
-    strcpy(cas->name, name);
-    ca_storage[id] = cas;
-
-    HASH_INSERT(ca_hash, CA, cas);
-  }
-
-  struct custom_attribute *ca = ralloc(p, &ca_class);
-  ca->fda = &(cas->fda);
-  ca->name = cas->name;
-  return ca;
-}
-
-const char *
-ea_custom_name(uint ea)
-{
-  uint id = EA_CUSTOM_ID(ea);
-  if (id >= ca_storage_max)
-    return NULL;
-
-  if (!ca_storage[id])
-    return NULL;
-
-  return ca_storage[id]->name;
-}
-
index 43c0444387469ed03e537a94e50b9e962d83f284..0273ef151e49bcff49bc98a088fdfb93269765cd 100644 (file)
@@ -70,13 +70,4 @@ void filters_dump_all(void);
 
 #define FF_SILENT 2                    /* Silent filter execution */
 
-/* Custom route attributes */
-struct custom_attribute {
-  resource r;
-  struct f_dynamic_attr *fda;
-  const char *name;
-};
-
-struct custom_attribute *ca_lookup(pool *p, const char *name, btype type);
-
 #endif
index 062756b1b276ab119cc209198baa414af38ac708..21e7330fd150ec4ee2d21934ed4fcea0fd232c25 100644 (file)
@@ -9,7 +9,109 @@ router id 62.168.0.1;
 /* We have to setup any protocol */
 protocol device { }
 
-
+/* Setting some custom attributes, enough to force BIRD to reallocate the attribute idmap */
+attribute int test_ca_int1;
+attribute int test_ca_int2;
+attribute int test_ca_int3;
+attribute int test_ca_int4;
+attribute int test_ca_int5;
+attribute int test_ca_int6;
+attribute int test_ca_int7;
+attribute int test_ca_int8;
+attribute int test_ca_int9;
+attribute int test_ca_int10;
+
+attribute ip test_ca_ip1;
+attribute ip test_ca_ip2;
+attribute ip test_ca_ip3;
+attribute ip test_ca_ip4;
+attribute ip test_ca_ip5;
+attribute ip test_ca_ip6;
+attribute ip test_ca_ip7;
+attribute ip test_ca_ip8;
+attribute ip test_ca_ip9;
+attribute ip test_ca_ip10;
+
+attribute quad test_ca_quad1;
+attribute quad test_ca_quad2;
+attribute quad test_ca_quad3;
+attribute quad test_ca_quad4;
+attribute quad test_ca_quad5;
+attribute quad test_ca_quad6;
+attribute quad test_ca_quad7;
+attribute quad test_ca_quad8;
+attribute quad test_ca_quad9;
+attribute quad test_ca_quad10;
+
+attribute bgppath test_ca_bgppath1;
+attribute bgppath test_ca_bgppath2;
+attribute bgppath test_ca_bgppath3;
+attribute bgppath test_ca_bgppath4;
+attribute bgppath test_ca_bgppath5;
+attribute bgppath test_ca_bgppath6;
+attribute bgppath test_ca_bgppath7;
+attribute bgppath test_ca_bgppath8;
+attribute bgppath test_ca_bgppath9;
+attribute bgppath test_ca_bgppath10;
+
+attribute clist test_ca_clist1;
+attribute clist test_ca_clist2;
+attribute clist test_ca_clist3;
+attribute clist test_ca_clist4;
+attribute clist test_ca_clist5;
+attribute clist test_ca_clist6;
+attribute clist test_ca_clist7;
+attribute clist test_ca_clist8;
+attribute clist test_ca_clist9;
+attribute clist test_ca_clist10;
+
+attribute eclist test_ca_eclist1;
+attribute eclist test_ca_eclist2;
+attribute eclist test_ca_eclist3;
+attribute eclist test_ca_eclist4;
+attribute eclist test_ca_eclist5;
+attribute eclist test_ca_eclist6;
+attribute eclist test_ca_eclist7;
+attribute eclist test_ca_eclist8;
+attribute eclist test_ca_eclist9;
+attribute eclist test_ca_eclist10;
+
+attribute lclist test_ca_lclist1;
+attribute lclist test_ca_lclist2;
+attribute lclist test_ca_lclist3;
+attribute lclist test_ca_lclist4;
+attribute lclist test_ca_lclist5;
+attribute lclist test_ca_lclist6;
+attribute lclist test_ca_lclist7;
+attribute lclist test_ca_lclist8;
+attribute lclist test_ca_lclist9;
+attribute lclist test_ca_lclist10;
+
+attribute lclist test_ca_lclist_max1;
+attribute lclist test_ca_lclist_max2;
+attribute lclist test_ca_lclist_max3;
+attribute lclist test_ca_lclist_max4;
+attribute lclist test_ca_lclist_max5;
+attribute lclist test_ca_lclist_max6;
+attribute lclist test_ca_lclist_max7;
+attribute lclist test_ca_lclist_max8;
+attribute lclist test_ca_lclist_max9;
+attribute lclist test_ca_lclist_max10;
+attribute lclist test_ca_lclist_max11;
+attribute lclist test_ca_lclist_max12;
+attribute lclist test_ca_lclist_max13;
+attribute lclist test_ca_lclist_max14;
+attribute lclist test_ca_lclist_max15;
+attribute lclist test_ca_lclist_max16;
+attribute lclist test_ca_lclist_max17;
+attribute lclist test_ca_lclist_max18;
+attribute lclist test_ca_lclist_max19;
+attribute lclist test_ca_lclist_max20;
+attribute lclist test_ca_lclist_max21;
+
+
+/* Uncomment this to get an error */
+#attribute int bgp_path;
 
 /*
  *     Common definitions and functions
@@ -1331,6 +1433,7 @@ function __test2()
 
 filter testf
 int j;
+bool t;
 {
        print "Heya, filtering route to ", net.ip, " prefixlen ", net.len, " source ", source;
        print "This route was from ", from;
@@ -1342,6 +1445,52 @@ int j;
        rip_metric = 14;
        unset(rip_metric);
 
+       test_ca_int1 = 42;
+       test_ca_ip2 = 1.3.5.7;
+       test_ca_quad3 = 2.4.6.8;
+       test_ca_bgppath4 = +empty+;
+       test_ca_clist5 = -empty-;
+       test_ca_eclist6 = --empty--;
+       test_ca_lclist7 = ---empty---;
+
+       igp_metric = 53;
+       babel_metric = 64;
+       t = defined(babel_router_id);
+       j = babel_seqno;
+
+       bgp_origin = ORIGIN_IGP;
+       bgp_path = +empty+;
+       bgp_next_hop = 3456:789a:bcde:f012::3456:789a;
+       bgp_med = 71;
+       bgp_local_pref = 942;
+       t = defined(bgp_atomic_aggr);
+       t = defined(bgp_aggregator);
+       bgp_community = -empty-;
+       bgp_originator_id = 9.7.5.3;
+       bgp_cluster_list = -empty-;
+       t = defined(bgp_mp_reach_nlri);
+       t = defined(bgp_mp_unreach_nlri);
+       bgp_ext_community = --empty--;
+       bgp_as4_path = +empty+;
+       t = defined(bgp_as4_aggregator);
+       t = defined(bgp_aigp);
+       bgp_large_community = ---empty---;
+       t = defined(bgp_mpls_label_stack);
+
+       ospf_metric1 = 64;
+       ospf_metric2 = 111;
+       ospf_tag = 654432;
+
+       radv_preference = RA_PREF_LOW;
+       radv_lifetime = 28;
+
+       rip_metric = 2;
+       rip_tag = 4;
+       t = defined(rip_from);
+
+       krt_source = 17;
+       krt_metric = 19;
+
 #      krt_lock_mtu = false;
 #      krt_lock_window = true;
 #      krt_lock_rtt = krt_lock_rttvar && krt_lock_sstresh || krt_lock_cwnd;
index e14d0b95191ceb4966f7cce70cbccecabbee1ef7..3070327de0ebfd9e63a6dfafc307972a6f46a77d 100644 (file)
@@ -55,8 +55,8 @@ t_ev_run_list(void)
 
   olock_init();
   timer_init();
-  io_init();
   rt_init();
+  io_init();
   if_init();
 //  roa_init();
   config_init();
index 325b1ecfccba14f130ee18e79f0176bd8aeadeb0..33eaec86abd5f79c8f5d9f36c6eef16b9e7bfb57 100644 (file)
@@ -41,8 +41,6 @@ struct linpool {
   uint total, total_large;
 };
 
-_Thread_local linpool *tmp_linpool;
-
 static void lp_free(resource *);
 static void lp_dump(resource *);
 static resource *lp_lookup(resource *, unsigned long);
index 89e559b4818a2ecae118e7e9b5370c6c05fedc65..c31d9889dbb12b8ebb2e460ca57bd60c71199522 100644 (file)
@@ -292,6 +292,25 @@ resource_init(void)
   tmp_init(&root_pool);
 }
 
+_Thread_local struct tmp_resources tmp_res;
+
+void
+tmp_init(pool *p)
+{
+  tmp_res.lp = lp_new_default(p);
+  tmp_res.parent = p;
+  tmp_res.pool = rp_new(p, "TMP");
+}
+
+void
+tmp_flush(void)
+{
+  lp_flush(tmp_linpool);
+  rfree(tmp_res.pool);
+  tmp_res.pool = rp_new(tmp_res.parent, "TMP");
+}
+
+
 /**
  * DOC: Memory blocks
  *
index a4e110a5144d086b35380b7548a2dca6c6774949..4cedbf005214e43466b7f59fdac3dc373943b1c5 100644 (file)
@@ -80,14 +80,21 @@ void lp_flush(linpool *);                   /* Free everything, but leave linpool */
 void lp_save(linpool *m, lp_state *p);         /* Save state */
 void lp_restore(linpool *m, lp_state *p);      /* Restore state */
 
-extern _Thread_local linpool *tmp_linpool;     /* Temporary linpool autoflushed regularily */
+struct tmp_resources {
+  pool *pool, *parent;
+  linpool *lp;
+};
+
+extern _Thread_local struct tmp_resources tmp_res;
 
+#define tmp_linpool    tmp_res.lp
 #define tmp_alloc(sz)  lp_alloc(tmp_linpool, sz)
 #define tmp_allocu(sz) lp_allocu(tmp_linpool, sz)
 #define tmp_allocz(sz) lp_allocz(tmp_linpool, sz)
 
-#define tmp_init(p)    tmp_linpool = lp_new_default(p)
-#define tmp_flush()    lp_flush(tmp_linpool)
+void tmp_init(pool *p);
+void tmp_flush(void);
+
 
 #define lp_new_default lp_new
 
index ad3f3fb74f844eef03352d3738d0f111728108da..f24fd55529deaa197a9cb1a40ba9fb58d7f992dc 100644 (file)
@@ -150,19 +150,7 @@ typedef struct eattr {
 } eattr;
 
 
-#define EA_CODE(proto,id) (((proto) << 8) | (id))
-#define EA_ID(ea) ((ea) & 0xff)
-#define EA_PROTO(ea) ((ea) >> 8)
-#define EA_CUSTOM(id) ((id) | EA_CUSTOM_BIT)
-#define EA_IS_CUSTOM(ea) ((ea) & EA_CUSTOM_BIT)
-#define EA_CUSTOM_ID(ea) ((ea) & ~EA_CUSTOM_BIT)
-
-const char *ea_custom_name(uint ea);
-
-#define EA_GEN_IGP_METRIC EA_CODE(PROTOCOL_NONE, 0)
-
 #define EA_CODE_MASK 0xffff
-#define EA_CUSTOM_BIT 0x8000
 #define EA_ALLOW_UNDEF 0x10000         /* ea_find: allow EAF_TYPE_UNDEF */
 #define EA_BIT(n) ((n) << 24)          /* Used in bitfield accessors */
 #define EA_BIT_GET(ea) ((ea) >> 24)
@@ -179,32 +167,67 @@ typedef struct ea_list {
 #define EALF_BISECT 2                  /* Use interval bisection for searching */
 #define EALF_CACHED 4                  /* Attributes belonging to cached rta */
 
+struct ea_class {
+#define EA_CLASS_INSIDE \
+  const char *name;                    /* Name (both print and filter) */ \
+  struct symbol *sym;                  /* Symbol to export to configs */ \
+  uint id;                             /* Autoassigned attribute ID */ \
+  uint uc;                             /* Reference count */ \
+  btype type;                          /* Data type ID */ \
+  uint readonly:1;                     /* This attribute can't be changed by filters */ \
+  uint conf:1;                         /* Requested by config */ \
+  void (*format)(const eattr *ea, byte *buf, uint size); \
+
+  EA_CLASS_INSIDE;
+};
+
+struct ea_class_ref {
+  resource r;
+  struct ea_class *class;
+};
+
+extern struct ea_class ea_gen_igp_metric;
+
+void ea_register_init(struct ea_class *);
+struct ea_class_ref *ea_register_alloc(pool *, struct ea_class);
+
+#define EA_REGISTER_ALL_HELPER(x)      ea_register_init(x);
+#define EA_REGISTER_ALL(...)           MACRO_FOREACH(EA_REGISTER_ALL_HELPER, __VA_ARGS__)
+
+struct ea_class *ea_class_find_by_id(uint id);
+struct ea_class *ea_class_find_by_name(const char *name);
+static inline struct ea_class *ea_class_self(struct ea_class *self) { return self; }
+#define ea_class_find(_arg)    _Generic((_arg), \
+  uint: ea_class_find_by_id, \
+  word: ea_class_find_by_id, \
+  char *: ea_class_find_by_name, \
+  const char *: ea_class_find_by_name, \
+  struct ea_class *: ea_class_self)(_arg)
+
 struct ea_walk_state {
   ea_list *eattrs;                     /* Ccurrent ea_list, initially set by caller */
   eattr *ea;                           /* Current eattr, initially NULL */
   u32 visited[4];                      /* Bitfield, limiting max to 128 */
 };
 
-eattr *ea_find(ea_list *, unsigned ea);
-eattr *ea_walk(struct ea_walk_state *s, uint id, uint max);
-
-/**
- * ea_get_int - fetch an integer attribute
- * @e: attribute list
- * @id: attribute ID
- * @def: default value
- *
- * This function is a shortcut for retrieving a value of an integer attribute
- * by calling ea_find() to find the attribute, extracting its value or returning
- * a provided default if no such attribute is present.
- */
-static inline u32
-ea_get_int(ea_list *e, unsigned id, u32 def)
+#define ea_find(_l, _arg)      _Generic((_arg), uint: ea_find_by_id, struct ea_class *: ea_find_by_class, char *: ea_find_by_name)(_l, _arg)
+eattr *ea_find_by_id(ea_list *, unsigned ea);
+static inline eattr *ea_find_by_class(ea_list *l, const struct ea_class *def)
+{ return ea_find_by_id(l, def->id); }
+static inline eattr *ea_find_by_name(ea_list *l, const char *name)
 {
-  eattr *a = ea_find(e, id);
-  return a ? a->u.data : def;
+  const struct ea_class *def = ea_class_find_by_name(name);
+  return def ? ea_find_by_class(l, def) : NULL;
 }
 
+#define ea_get_int(_l, _ident, _def)  ({ \
+    struct ea_class *cls = ea_class_find((_ident)); \
+    ASSERT_DIE(cls->type & EAF_EMBEDDED); \
+    const eattr *ea = ea_find((_l), cls->id); \
+    (ea ? ea->u.data : (_def)); \
+    })
+
+eattr *ea_walk(struct ea_walk_state *s, uint id, uint max);
 void ea_dump(ea_list *);
 int ea_same(ea_list *x, ea_list *y);   /* Test whether two ea_lists are identical */
 uint ea_hash(ea_list *e);      /* Calculate 16-bit hash value */
@@ -219,19 +242,22 @@ void ea_list_copy(ea_list *dest, ea_list *src, uint size);
 
 #define EA_LOCAL_LIST(N)  struct { ea_list l; eattr a[N]; }
 
-#define EA_LITERAL_EMBEDDED(_id, _type, _flags, _val) ({ \
+#define EA_LITERAL_EMBEDDED(_class, _flags, _val) ({ \
+    btype _type = (_class)->type; \
     ASSERT_DIE(_type & EAF_EMBEDDED); \
-    EA_LITERAL_GENERIC(_id, _type, _flags, .u.i = _val); \
+    EA_LITERAL_GENERIC((_class)->id, _type, _flags, .u.i = _val); \
     })
 
-#define EA_LITERAL_STORE_ADATA(_id, _type, _flags, _buf, _len) ({ \
+#define EA_LITERAL_STORE_ADATA(_class, _flags, _buf, _len) ({ \
+    btype _type = (_class)->type; \
     ASSERT_DIE(!(_type & EAF_EMBEDDED)); \
-    EA_LITERAL_GENERIC(_id, _type, _flags, .u.ad = tmp_store_adata((_buf), (_len))); \
+    EA_LITERAL_GENERIC((_class)->id, _type, _flags, .u.ad = tmp_store_adata((_buf), (_len))); \
     })
 
-#define EA_LITERAL_DIRECT_ADATA(_id, _type, _flags, _adata) ({ \
+#define EA_LITERAL_DIRECT_ADATA(_class, _flags, _adata) ({ \
+    btype _type = (_class)->type; \
     ASSERT_DIE(!(_type & EAF_EMBEDDED)); \
-    EA_LITERAL_GENERIC(_id, _type, _flags, .u.ad = _adata); \
+    EA_LITERAL_GENERIC((_class)->id, _type, _flags, .u.ad = _adata); \
     })
 
 #define EA_LITERAL_GENERIC(_id, _type, _flags, ...) \
@@ -253,19 +279,19 @@ ea_set_attr(ea_list **to, eattr a)
 }
 
 static inline void
-ea_unset_attr(ea_list **to, _Bool local, uint code)
+ea_unset_attr(ea_list **to, _Bool local, const struct ea_class *def)
 {
-  ea_set_attr(to, EA_LITERAL_GENERIC(code, 0, 0,
+  ea_set_attr(to, EA_LITERAL_GENERIC(def->id, 0, 0,
        .fresh = local, .originated = local, .undef = 1));
 }
 
 static inline void
-ea_set_attr_u32(ea_list **to, uint id, uint flags, uint type, u32 data)
-{ ea_set_attr(to, EA_LITERAL_EMBEDDED(id, type, flags, data)); }
+ea_set_attr_u32(ea_list **to, const struct ea_class *def, uint flags, u64 data)
+{ ea_set_attr(to, EA_LITERAL_EMBEDDED(def, flags, data)); }
 
 static inline void
-ea_set_attr_data(ea_list **to, uint id, uint flags, uint type, void *data, uint len)
-{ ea_set_attr(to, EA_LITERAL_STORE_ADATA(id, type, flags, data, len)); }
+ea_set_attr_data(ea_list **to, const struct ea_class *def, uint flags, void *data, uint len)
+{ ea_set_attr(to, EA_LITERAL_STORE_ADATA(def, flags, data, len)); }
 
 
 #define NEXTHOP_MAX_SIZE (sizeof(struct nexthop) + sizeof(u32)*MPLS_MAX_LABEL_STACK)
index 12fd01a24f5c76e8420fec27d8715eb33a15c49a..c291350622cd0597c7cd8397305774dc626f12ac 100644 (file)
@@ -120,7 +120,7 @@ CF_KEYWORDS(PASSWORD, KEY, FROM, PASSIVE, TO, ID, EVENTS, PACKETS, PROTOCOLS, CH
 CF_KEYWORDS(ALGORITHM, KEYED, HMAC, MD5, SHA1, SHA256, SHA384, SHA512, BLAKE2S128, BLAKE2S256, BLAKE2B256, BLAKE2B512)
 CF_KEYWORDS(PRIMARY, STATS, COUNT, BY, FOR, IN, COMMANDS, PREEXPORT, NOEXPORT, EXPORTED, GENERATE)
 CF_KEYWORDS(BGP, PASSWORDS, DESCRIPTION)
-CF_KEYWORDS(RELOAD, IN, OUT, MRTDUMP, MESSAGES, RESTRICT, MEMORY, IGP_METRIC, CLASS, DSCP)
+CF_KEYWORDS(RELOAD, IN, OUT, MRTDUMP, MESSAGES, RESTRICT, MEMORY, CLASS, DSCP)
 CF_KEYWORDS(TIMEFORMAT, ISO, SHORT, LONG, ROUTE, PROTOCOL, BASE, LOG, S, MS, US)
 CF_KEYWORDS(GRACEFUL, RESTART, WAIT, MAX, FLUSH, AS)
 CF_KEYWORDS(MIN, IDLE, RX, TX, INTERVAL, MULTIPLIER, PASSIVE)
@@ -921,9 +921,6 @@ proto_patt2:
  | TEXT { $$.ptr = $1; $$.patt = 1; }
  ;
 
-dynamic_attr: IGP_METRIC { $$ = f_new_dynamic_attr(T_INT, EA_GEN_IGP_METRIC); } ;
-
-
 CF_CODE
 
 CF_END
index 95c319db8fddc3dcfa74ad349cc03d585b58d508..72613f8d74074c5672648148b3811c81ead619c8 100644 (file)
@@ -26,7 +26,6 @@ pool *proto_pool;
 list STATIC_LIST_INIT(proto_list);
 
 static list STATIC_LIST_INIT(protocol_list);
-struct protocol *class_to_protocol[PROTOCOL__MAX];
 
 #define CD(c, msg, args...) ({ if (c->debug & D_STATES) log(L_TRACE "%s.%s: " msg, c->proto->name, c->name ?: "?", ## args); })
 #define PD(p, msg, args...) ({ if (p->debug & D_STATES) log(L_TRACE "%s: " msg, p->name, ## args); })
@@ -1637,9 +1636,6 @@ void
 proto_build(struct protocol *p)
 {
   add_tail(&protocol_list, &p->n);
-  ASSERT(p->class);
-  ASSERT(!class_to_protocol[p->class]);
-  class_to_protocol[p->class] = p;
 }
 
 /* FIXME: convert this call to some protocol hook */
index 7fb089926d0e678e874c9a8841e376c8309a961a..8f0cc4b4eb7d0389ed9408bb470237ddab33b701 100644 (file)
@@ -37,38 +37,20 @@ struct symbol;
  *     Routing Protocol
  */
 
-enum protocol_class {
-  PROTOCOL_NONE,
-  PROTOCOL_BABEL,
-  PROTOCOL_BFD,
-  PROTOCOL_BGP,
-  PROTOCOL_DEVICE,
-  PROTOCOL_DIRECT,
-  PROTOCOL_KERNEL,
-  PROTOCOL_OSPF,
-  PROTOCOL_MRT,
-  PROTOCOL_PERF,
-  PROTOCOL_PIPE,
-  PROTOCOL_RADV,
-  PROTOCOL_RIP,
-  PROTOCOL_RPKI,
-  PROTOCOL_STATIC,
-  PROTOCOL__MAX
-};
-
-extern struct protocol *class_to_protocol[PROTOCOL__MAX];
 
 struct protocol {
   node n;
   char *name;
   char *template;                      /* Template for automatic generation of names */
   int name_counter;                    /* Counter for automatic name generation */
-  enum protocol_class class;           /* Machine readable protocol class */
   uint preference;                     /* Default protocol preference */
   uint channel_mask;                   /* Mask of accepted channel types (NB_*) */
   uint proto_size;                     /* Size of protocol data structure */
   uint config_size;                    /* Size of protocol config data structure */
 
+  uint eattr_begin;                    /* First ID of registered eattrs */
+  uint eattr_end;                      /* End of eattr id zone */
+
   void (*preconfig)(struct protocol *, struct config *);       /* Just before configuring */
   void (*postconfig)(struct proto_config *);                   /* After configuring each instance */
   struct proto * (*init)(struct proto_config *);               /* Create new instance */
@@ -79,7 +61,7 @@ struct protocol {
   void (*cleanup)(struct proto *);             /* Called after shutdown when protocol became hungry/down */
   void (*get_status)(struct proto *, byte *buf); /* Get instance status (for `show protocols' command) */
   void (*get_route_info)(struct rte *, byte *buf); /* Get route information (for `show route' command) */
-  int (*get_attr)(const struct eattr *, byte *buf, int buflen);        /* ASCIIfy dynamic attribute (returns GA_*) */
+//  int (*get_attr)(const struct eattr *, byte *buf, int buflen);      /* ASCIIfy dynamic attribute (returns GA_*) */
   void (*show_proto_info)(struct proto *);     /* Show protocol info (for `show protocols all' command) */
   void (*copy_config)(struct proto_config *, struct proto_config *);   /* Copy config from given protocol instance */
 };
index afa95eec6316d85d3ab9a77dd7eafaa74a1d9060..a763db4d50251d2a3bf9ecd5acb75581c460886d 100644 (file)
 
 const adata null_adata;                /* adata of length 0 */
 
+struct ea_class ea_gen_igp_metric = {
+  .name = "igp_metric",
+  .type = T_INT,
+};
+
 const char * const rta_src_names[RTS_MAX] = {
   [RTS_STATIC]         = "static",
   [RTS_INHERIT]                = "inherit",
@@ -401,6 +406,117 @@ nexthop_free(struct nexthop *o)
  *     Extended Attributes
  */
 
+#define EA_CLASS_INITIAL_MAX   128
+static struct ea_class **ea_class_global = NULL;
+static uint ea_class_max;
+static struct idm ea_class_idm;
+
+/* Config parser lex register function */
+void ea_lex_register(struct ea_class *def);
+void ea_lex_unregister(struct ea_class *def);
+
+static void
+ea_class_free(struct ea_class *cl)
+{
+  /* No more ea class references. Unregister the attribute. */
+  idm_free(&ea_class_idm, cl->id);
+  ea_class_global[cl->id] = NULL;
+  ea_lex_unregister(cl);
+}
+
+static void
+ea_class_ref_free(resource *r)
+{
+  struct ea_class_ref *ref = SKIP_BACK(struct ea_class_ref, r, r);
+  if (!--ref->class->uc)
+    ea_class_free(ref->class);
+}
+
+static void
+ea_class_ref_dump(resource *r)
+{
+  struct ea_class_ref *ref = SKIP_BACK(struct ea_class_ref, r, r);
+  debug("name \"%s\", type=%d\n", ref->class->name, ref->class->type);
+}
+
+static struct resclass ea_class_ref_class = {
+  .name = "Attribute class reference",
+  .size = sizeof(struct ea_class_ref),
+  .free = ea_class_ref_free,
+  .dump = ea_class_ref_dump,
+  .lookup = NULL,
+  .memsize = NULL,
+};
+
+static void
+ea_class_init(void)
+{
+  idm_init(&ea_class_idm, rta_pool, EA_CLASS_INITIAL_MAX);
+  ea_class_global = mb_allocz(rta_pool,
+      sizeof(*ea_class_global) * (ea_class_max = EA_CLASS_INITIAL_MAX));
+}
+
+static struct ea_class_ref *
+ea_ref_class(pool *p, struct ea_class *def)
+{
+  def->uc++;
+  struct ea_class_ref *ref = ralloc(p, &ea_class_ref_class);
+  ref->class = def;
+  return ref;
+}
+
+static struct ea_class_ref *
+ea_register(pool *p, struct ea_class *def)
+{
+  def->id = idm_alloc(&ea_class_idm);
+
+  ASSERT_DIE(ea_class_global);
+  while (def->id >= ea_class_max)
+    ea_class_global = mb_realloc(ea_class_global, sizeof(*ea_class_global) * (ea_class_max *= 2));
+
+  ASSERT_DIE(def->id < ea_class_max);
+  ea_class_global[def->id] = def;
+
+  ea_lex_register(def);
+
+  return ea_ref_class(p, def);
+}
+
+struct ea_class_ref *
+ea_register_alloc(pool *p, struct ea_class cl)
+{
+  struct ea_class *clp = ea_class_find_by_name(cl.name);
+  if (clp && clp->type == cl.type)
+    return ea_ref_class(p, clp);
+
+  uint namelen = strlen(cl.name) + 1;
+
+  struct {
+    struct ea_class cl;
+    char name[0];
+  } *cla = mb_alloc(rta_pool, sizeof(struct ea_class) + namelen);
+  cla->cl = cl;
+  memcpy(cla->name, cl.name, namelen);
+  cla->cl.name = cla->name;
+
+  return ea_register(p, &cla->cl);
+}
+
+void
+ea_register_init(struct ea_class *clp)
+{
+  ASSERT_DIE(!ea_class_find_by_name(clp->name));
+  ea_register(&root_pool, clp);
+}
+
+struct ea_class *
+ea_class_find_by_id(uint id)
+{
+  ASSERT_DIE(id < ea_class_max);
+  ASSERT_DIE(ea_class_global[id]);
+  return ea_class_global[id];
+}
+
 static inline eattr *
 ea__find(ea_list *e, unsigned id)
 {
@@ -444,7 +560,7 @@ ea__find(ea_list *e, unsigned id)
  * to its &eattr structure or %NULL if no such attribute exists.
  */
 eattr *
-ea_find(ea_list *e, unsigned id)
+ea_find_by_id(ea_list *e, unsigned id)
 {
   eattr *a = ea__find(e, id & EA_CODE_MASK);
 
@@ -784,26 +900,44 @@ ea_list_copy(ea_list *n, ea_list *o, uint elen)
   ASSERT_DIE(adpos == elen);
 }
 
-static inline void
-ea_free(ea_list *o)
+static void
+ea_list_ref(ea_list *l)
 {
-  if (o)
+  for(uint i=0; i<l->count; i++)
     {
-      ASSERT(!o->next);
-      mb_free(o);
+      eattr *a = &l->attrs[i];
+      ASSERT_DIE(a->id < ea_class_max);
+
+      struct ea_class *cl = ea_class_global[a->id];
+      ASSERT_DIE(cl && cl->uc);
+      cl->uc++;
     }
 }
 
-static int
-get_generic_attr(const eattr *a, byte **buf, int buflen UNUSED)
+static void
+ea_list_unref(ea_list *l)
 {
-  if (a->id == EA_GEN_IGP_METRIC)
+  for(uint i=0; i<l->count; i++)
     {
-      *buf += bsprintf(*buf, "igp_metric");
-      return GA_NAME;
+      eattr *a = &l->attrs[i];
+      ASSERT_DIE(a->id < ea_class_max);
+
+      struct ea_class *cl = ea_class_global[a->id];
+      ASSERT_DIE(cl && cl->uc);
+      if (!--cl->uc)
+       ea_class_free(cl);
     }
+}
 
-  return GA_UNKNOWN;
+static inline void
+ea_free(ea_list *o)
+{
+  if (o)
+    {
+      ea_list_unref(o);
+      ASSERT(!o->next);
+      mb_free(o);
+    }
 }
 
 void
@@ -905,47 +1039,27 @@ ea_show_lc_set(struct cli *c, const struct adata *ad, byte *pos, byte *buf, byte
 void
 ea_show(struct cli *c, const eattr *e)
 {
-  struct protocol *p;
-  int status = GA_UNKNOWN;
   const struct adata *ad = (e->type & EAF_EMBEDDED) ? NULL : e->u.ptr;
   byte buf[CLI_MSG_SIZE];
   byte *pos = buf, *end = buf + sizeof(buf);
 
-  if (EA_IS_CUSTOM(e->id))
-    {
-      const char *name = ea_custom_name(e->id);
-      if (name)
-        {
-         pos += bsprintf(pos, "%s", name);
-         status = GA_NAME;
-       }
-      else
-       pos += bsprintf(pos, "%02x.", EA_PROTO(e->id));
-    }
-  else if (p = class_to_protocol[EA_PROTO(e->id)])
-    {
-      pos += bsprintf(pos, "%s.", p->name);
-      if (p->get_attr)
-       status = p->get_attr(e, pos, end - pos);
-      pos += strlen(pos);
-    }
-  else if (EA_PROTO(e->id))
-    pos += bsprintf(pos, "%02x.", EA_PROTO(e->id));
-  else
-    status = get_generic_attr(e, &pos, end - pos);
+  ASSERT_DIE(e->id < ea_class_max);
 
-  if (status < GA_NAME)
-    pos += bsprintf(pos, "%02x", EA_ID(e->id));
-  if (status < GA_FULL)
-    {
-      *pos++ = ':';
-      *pos++ = ' ';
+  struct ea_class *cls = ea_class_global[e->id];
+  ASSERT_DIE(cls);
 
-      if (e->undef)
-       bsprintf(pos, "undefined");
-      else
-      switch (e->type)
-       {
+  pos += bsprintf(pos, "%s", cls->name);
+
+  *pos++ = ':';
+  *pos++ = ' ';
+
+  if (e->undef)
+    bsprintf(pos, "undefined (should not happen)");
+  else if (cls->format)
+    cls->format(e, buf, end - buf);
+  else
+    switch (e->type)
+      {
        case T_INT:
          bsprintf(pos, "%u", e->u.data);
          break;
@@ -970,13 +1084,10 @@ ea_show(struct cli *c, const eattr *e)
        case T_LCLIST:
          ea_show_lc_set(c, ad, pos, buf, end);
          return;
-       case T_IFACE:
-         bsprintf(pos, "%s", ((struct iface *) e->u.ptr)->name);
-         return;
        default:
          bsprintf(pos, "<type %02x>", e->type);
-       }
-    }
+      }
+
   cli_printf(c, -1012, "\t%s", buf);
 }
 
@@ -1006,7 +1117,7 @@ ea_dump(ea_list *e)
       for(i=0; i<e->count; i++)
        {
          eattr *a = &e->attrs[i];
-         debug(" %02x:%02x.%02x", EA_PROTO(a->id), EA_ID(a->id), a->flags);
+         debug(" %04x.%02x", a->id, a->flags);
          debug("=%c",
              "?iO?IRP???S??pE?"
              "??L???N?????????"
@@ -1157,6 +1268,7 @@ rta_copy(rta *o)
   uint elen = ea_list_size(o->eattrs);
   r->eattrs = mb_alloc(rta_pool, elen);
   ea_list_copy(r->eattrs, o->eattrs, elen);
+  ea_list_ref(r->eattrs);
   r->eattrs->flags |= EALF_CACHED;
   return r;
 }
@@ -1357,6 +1469,9 @@ rta_init(void)
 
   rta_alloc_hash();
   rte_src_init();
+  ea_class_init();
+
+  ea_register_init(&ea_gen_igp_metric);
 }
 
 /*
index 825333212b9106274168356bf5cc86d86fc969ed..bdf8584d70e9ea376fe63e7c63c4c5940ae1abf0 100644 (file)
@@ -185,7 +185,6 @@ dev_copy_config(struct proto_config *dest, struct proto_config *src)
 struct protocol proto_device = {
   .name =              "Direct",
   .template =          "direct%d",
-  .class =             PROTOCOL_DIRECT,
   .preference =                DEF_PREF_DIRECT,
   .channel_mask =      NB_IP | NB_IP6_SADR,
   .proto_size =                sizeof(struct rt_dev_proto),
index 049b7a7f943d76cc60d4c8056856891cafdbeae1..af59d63b53ca7d951521c7e405c438610b2061db 100644 (file)
@@ -2536,14 +2536,14 @@ net_flow_has_dst_prefix(const net_addr *n)
 static inline int
 rta_as_path_is_empty(rta *a)
 {
-  eattr *e = ea_find(a->eattrs, EA_CODE(PROTOCOL_BGP, BA_AS_PATH));
+  eattr *e = ea_find(a->eattrs, "bgp_path");
   return !e || (as_path_getlen(e->u.ptr) == 0);
 }
 
 static inline u32
 rta_get_first_asn(rta *a)
 {
-  eattr *e = ea_find(a->eattrs, EA_CODE(PROTOCOL_BGP, BA_AS_PATH));
+  eattr *e = ea_find(a->eattrs, "bgp_path");
   u32 asn;
 
   return (e && as_path_get_first_regular(e->u.ptr, &asn)) ? asn : 0;
@@ -2587,8 +2587,8 @@ rt_flowspec_check(rtable *tab_ip, rtable *tab_flow, const net_addr *n, rta *a, i
     return 0;
 
   /* Find ORIGINATOR_ID values */
-  u32 orig_a = ea_get_int(a->eattrs, EA_CODE(PROTOCOL_BGP, BA_ORIGINATOR_ID), 0);
-  u32 orig_b = ea_get_int(rb->attrs->eattrs, EA_CODE(PROTOCOL_BGP, BA_ORIGINATOR_ID), 0);
+  u32 orig_a = ea_get_int(a->eattrs, "bgp_originator_id", 0);
+  u32 orig_b = ea_get_int(rb->attrs->eattrs, "bgp_originator_id", 0);
 
   /* Originator is either ORIGINATOR_ID (if present), or BGP neighbor address (if not) */
   if ((orig_a != orig_b) || (!orig_a && !orig_b && !ipa_equal(a->from, rb->attrs->from)))
@@ -3458,7 +3458,7 @@ if_local_addr(ip_addr a, struct iface *i)
 u32
 rt_get_igp_metric(rte *rt)
 {
-  eattr *ea = ea_find(rt->attrs->eattrs, EA_GEN_IGP_METRIC);
+  eattr *ea = ea_find(rt->attrs->eattrs, "igp_metric");
 
   if (ea)
     return ea->u.data;
index db710a0de43061e02e1fd7108d5755fcf233bebb..97dca4aceb538becb0d7716e98bfec880aff3a8c 100644 (file)
@@ -37,6 +37,7 @@
 
 #include <stdlib.h>
 #include "babel.h"
+#include "lib/macro.h"
 
 #define LOG_PKT_AUTH(msg, args...) \
   log_rl(&p->log_pkt_tbf, L_AUTH "%s: " msg, p->p.name, args)
@@ -58,6 +59,8 @@ static void babel_update_cost(struct babel_neighbor *n);
 static inline void babel_kick_timer(struct babel_proto *p);
 static inline void babel_iface_kick_timer(struct babel_iface *ifa);
 
+static struct ea_class ea_babel_metric, ea_babel_router_id, ea_babel_seqno;
+
 /*
  *     Functions to maintain data structures
  */
@@ -646,9 +649,9 @@ babel_announce_rte(struct babel_proto *p, struct babel_entry *e)
     } eattrs = {
       .l.count = 3,
       .a = {
-       EA_LITERAL_EMBEDDED(EA_BABEL_METRIC, T_INT, 0, r->metric),
-       EA_LITERAL_STORE_ADATA(EA_BABEL_ROUTER_ID, T_OPAQUE, 0, &r->router_id, sizeof(r->router_id)),
-       EA_LITERAL_EMBEDDED(EA_BABEL_SEQNO, T_INT, 0, r->seqno),
+       EA_LITERAL_EMBEDDED(&ea_babel_metric, 0, r->metric),
+       EA_LITERAL_STORE_ADATA(&ea_babel_router_id, 0, &r->router_id, sizeof(r->router_id)),
+       EA_LITERAL_EMBEDDED(&ea_babel_seqno, 0, r->seqno),
       }
     };
 
@@ -2018,38 +2021,40 @@ static void
 babel_get_route_info(rte *rte, byte *buf)
 {
   u64 rid = 0;
-  eattr *e = ea_find(rte->attrs->eattrs, EA_BABEL_ROUTER_ID);
+  eattr *e = ea_find(rte->attrs->eattrs, &ea_babel_router_id);
   if (e)
     memcpy(&rid, e->u.ptr->data, sizeof(u64));
 
   buf += bsprintf(buf, " (%d/%d) [%lR]", rte->attrs->pref,
-      ea_get_int(rte->attrs->eattrs, EA_BABEL_METRIC, BABEL_INFINITY), rid);
+      ea_get_int(rte->attrs->eattrs, &ea_babel_metric, BABEL_INFINITY), rid);
 }
 
-static int
-babel_get_attr(const eattr *a, byte *buf, int buflen UNUSED)
+static void
+babel_router_id_format(const eattr *a, byte *buf, uint len)
 {
-  switch (a->id)
-  {
-  case EA_BABEL_SEQNO:
-    return GA_FULL;
+  u64 rid = 0;
+  memcpy(&rid, a->u.ptr->data, sizeof(u64));
+  bsnprintf(buf, len, "%lR", rid);
+}
 
-  case EA_BABEL_METRIC:
-    bsprintf(buf, "metric: %d", a->u.data);
-    return GA_FULL;
+static struct ea_class ea_babel_metric = {
+  .name = "babel_metric",
+  .type = T_INT,
+};
 
-  case EA_BABEL_ROUTER_ID:
-  {
-    u64 rid = 0;
-    memcpy(&rid, a->u.ptr->data, sizeof(u64));
-    bsprintf(buf, "router_id: %lR", rid);
-    return GA_FULL;
-  }
+static struct ea_class ea_babel_router_id = {
+  .name = "babel_router_id",
+  .type = T_OPAQUE,
+  .readonly = 1,
+  .format = babel_router_id_format,
+};
+
+static struct ea_class ea_babel_seqno = {
+  .name = "babel_seqno",
+  .type = T_INT,
+  .readonly = 1,
+};
 
-  default:
-    return GA_UNKNOWN;
-  }
-}
 
 void
 babel_show_interfaces(struct proto *P, const char *iff)
@@ -2272,13 +2277,13 @@ babel_rt_notify(struct proto *P, struct channel *c UNUSED, struct network *net,
   {
     /* Update */
     uint rt_seqno;
-    uint rt_metric = ea_get_int(new->attrs->eattrs, EA_BABEL_METRIC, 0);
+    uint rt_metric = ea_get_int(new->attrs->eattrs, &ea_babel_metric, 0);
     u64 rt_router_id = 0;
 
     if (new->src->proto == P)
     {
-      rt_seqno = ea_find(new->attrs->eattrs, EA_BABEL_SEQNO)->u.data;
-      eattr *e = ea_find(new->attrs->eattrs, EA_BABEL_ROUTER_ID);
+      rt_seqno = ea_get_int(new->attrs->eattrs, &ea_babel_seqno, 0);
+      eattr *e = ea_find(new->attrs->eattrs, &ea_babel_router_id);
       if (e)
        memcpy(&rt_router_id, e->u.ptr->data, sizeof(u64));
     }
@@ -2329,8 +2334,8 @@ babel_rt_notify(struct proto *P, struct channel *c UNUSED, struct network *net,
 static int
 babel_rte_better(struct rte *new, struct rte *old)
 {
-  uint new_metric = ea_find(new->attrs->eattrs, EA_BABEL_SEQNO)->u.data;
-  uint old_metric = ea_find(old->attrs->eattrs, EA_BABEL_SEQNO)->u.data;
+  uint new_metric = ea_get_int(new->attrs->eattrs, &ea_babel_metric, BABEL_INFINITY);
+  uint old_metric = ea_get_int(old->attrs->eattrs, &ea_babel_metric, BABEL_INFINITY);
 
   return new_metric < old_metric;
 }
@@ -2338,7 +2343,7 @@ babel_rte_better(struct rte *new, struct rte *old)
 static u32
 babel_rte_igp_metric(struct rte *rt)
 {
-  return ea_get_int(rt->attrs->eattrs, EA_BABEL_METRIC, BABEL_INFINITY);
+  return ea_get_int(rt->attrs->eattrs, &ea_babel_metric, BABEL_INFINITY);
 }
 
 
@@ -2466,11 +2471,9 @@ babel_reconfigure(struct proto *P, struct proto_config *CF)
   return 1;
 }
 
-
 struct protocol proto_babel = {
   .name =              "Babel",
   .template =          "babel%d",
-  .class =             PROTOCOL_BABEL,
   .preference =                DEF_PREF_BABEL,
   .channel_mask =      NB_IP | NB_IP6_SADR,
   .proto_size =                sizeof(struct babel_proto),
@@ -2482,11 +2485,16 @@ struct protocol proto_babel = {
   .shutdown =          babel_shutdown,
   .reconfigure =       babel_reconfigure,
   .get_route_info =    babel_get_route_info,
-  .get_attr =          babel_get_attr
 };
 
 void
 babel_build(void)
 {
   proto_build(&proto_babel);
+
+  EA_REGISTER_ALL(
+      &ea_babel_metric,
+      &ea_babel_router_id,
+      &ea_babel_seqno
+      );
 }
index 00814641566fa4e1e8a2d27c95d1b636336b29c1..a980d1da84f4c5cb8cedc4e793351794c89551ef 100644 (file)
 #include "lib/string.h"
 #include "lib/timer.h"
 
-#define EA_BABEL_METRIC                EA_CODE(PROTOCOL_BABEL, 0)
-#define EA_BABEL_ROUTER_ID     EA_CODE(PROTOCOL_BABEL, 1)
-#define EA_BABEL_SEQNO         EA_CODE(PROTOCOL_BABEL, 2)
-
 #define BABEL_MAGIC            42
 #define BABEL_VERSION          2
 #define BABEL_PORT             6696
index fa745993b21d2211bf281878243a08d39581a553..82419b20524b7f2f1e03429c66ead90db93e9367 100644 (file)
@@ -163,8 +163,6 @@ babel_iface_opt_list:
 babel_iface:
   babel_iface_start iface_patt_list_nopx babel_iface_opt_list babel_iface_finish;
 
-dynamic_attr: BABEL_METRIC { $$ = f_new_dynamic_attr(T_INT, EA_BABEL_METRIC); } ;
-
 CF_CLI_HELP(SHOW BABEL, ..., [[Show information about Babel protocol]]);
 
 CF_CLI(SHOW BABEL INTERFACES, optproto opttext, [<name>] [\"<interface>\"], [[Show information about Babel interfaces]])
index d1e97cd5c8bf80644d6c25681ad13b3335a05d59..1a2104ade352787dec1f926ac7ce62aca8e9f978 100644 (file)
@@ -1170,7 +1170,6 @@ bfd_show_sessions(struct proto *P)
 struct protocol proto_bfd = {
   .name =              "BFD",
   .template =          "bfd%d",
-  .class =             PROTOCOL_BFD,
   .proto_size =                sizeof(struct bfd_proto),
   .config_size =       sizeof(struct bfd_config),
   .init =              bfd_init,
index 4c67b1619d7a638035436cee085dd975f13bd6ff..72a4f68ad622718e7a76e56c21a35ef5b6410879 100644 (file)
@@ -21,6 +21,7 @@
 #include "lib/resource.h"
 #include "lib/string.h"
 #include "lib/unaligned.h"
+#include "lib/macro.h"
 
 #include "bgp.h"
 
  * format - Optional hook that converts eattr to textual representation.
  */
 
-
-struct bgp_attr_desc {
-  const char *name;
-  uint type;
-  uint flags;
-  void (*export)(struct bgp_export_state *s, eattr *a);
-  int  (*encode)(struct bgp_write_state *s, eattr *a, byte *buf, uint size);
-  void (*decode)(struct bgp_parse_state *s, uint code, uint flags, byte *data, uint len, ea_list **to);
-  void (*format)(const eattr *ea, byte *buf, uint size);
+union bgp_attr_desc {
+  struct ea_class class;
+  struct {
+    EA_CLASS_INSIDE;
+    uint flags;
+    void (*export)(struct bgp_export_state *s, eattr *a);
+    int  (*encode)(struct bgp_write_state *s, eattr *a, byte *buf, uint size);
+    void (*decode)(struct bgp_parse_state *s, uint code, uint flags, byte *data, uint len, ea_list **to);
+  };
 };
 
-static const struct bgp_attr_desc bgp_attr_table[];
+static union bgp_attr_desc bgp_attr_table[];
+static inline const union bgp_attr_desc *bgp_find_attr_desc(eattr *a)
+{
+  const struct ea_class *class = ea_class_find(a->id);
+
+  if ((class < &bgp_attr_table[0].class) || (class >= &bgp_attr_table[BGP_ATTR_MAX].class))
+    return NULL;
+
+  return (const union bgp_attr_desc *) class;
+}
 
-static inline int bgp_attr_known(uint code);
+#define BGP_EA_ID(code)        (bgp_attr_table[code].id)
+#define EA_BGP_ID(code)        (((union bgp_attr_desc *) ea_class_find(code)) - bgp_attr_table)
 
 void bgp_set_attr_u32(ea_list **to, uint code, uint flags, u32 val)
 {
-  ASSERT(bgp_attr_known(code));
+  const union bgp_attr_desc *desc = &bgp_attr_table[code];
 
   ea_set_attr(to, EA_LITERAL_EMBEDDED(
-       EA_CODE(PROTOCOL_BGP, code),
-       bgp_attr_table[code].type,
+       &desc->class,
        flags & ~BAF_EXT_LEN,
        val
        ));
@@ -93,11 +103,10 @@ void bgp_set_attr_u32(ea_list **to, uint code, uint flags, u32 val)
 
 void bgp_set_attr_ptr(ea_list **to, uint code, uint flags, const struct adata *ad)
 {
-  ASSERT(bgp_attr_known(code));
+  const union bgp_attr_desc *desc = &bgp_attr_table[code];
 
   ea_set_attr(to, EA_LITERAL_DIRECT_ADATA(
-       EA_CODE(PROTOCOL_BGP, code),
-       bgp_attr_table[code].type,
+       &desc->class,
        flags & ~BAF_EXT_LEN,
        ad
        ));
@@ -106,17 +115,23 @@ void bgp_set_attr_ptr(ea_list **to, uint code, uint flags, const struct adata *a
 void
 bgp_set_attr_data(ea_list **to, uint code, uint flags, void *data, uint len)
 {
-  ASSERT(bgp_attr_known(code));
+  const union bgp_attr_desc *desc = &bgp_attr_table[code];
 
   ea_set_attr(to, EA_LITERAL_STORE_ADATA(
-       EA_CODE(PROTOCOL_BGP, code),
-       bgp_attr_table[code].type,
+       &desc->class,
        flags & ~BAF_EXT_LEN,
        data,
        len
        ));
 }
 
+void
+bgp_unset_attr(ea_list **to, uint code)
+{
+  const union bgp_attr_desc *desc = &bgp_attr_table[code];
+  ea_unset_attr(to, 0, &desc->class);
+}
+
 #define REPORT(msg, args...) \
   ({ log(L_REMOTE "%s: " msg, s->proto->p.name, ## args); })
 
@@ -172,7 +187,7 @@ bgp_encode_u8(struct bgp_write_state *s UNUSED, eattr *a, byte *buf, uint size)
   if (size < (3+1))
     return -1;
 
-  bgp_put_attr_hdr3(buf, EA_ID(a->id), a->flags, 1);
+  bgp_put_attr_hdr3(buf, EA_BGP_ID(a->id), a->flags, 1);
   buf[3] = a->u.data;
 
   return 3+1;
@@ -184,7 +199,7 @@ bgp_encode_u32(struct bgp_write_state *s UNUSED, eattr *a, byte *buf, uint size)
   if (size < (3+4))
     return -1;
 
-  bgp_put_attr_hdr3(buf, EA_ID(a->id), a->flags, 4);
+  bgp_put_attr_hdr3(buf, EA_BGP_ID(a->id), a->flags, 4);
   put_u32(buf+3, a->u.data);
 
   return 3+4;
@@ -198,7 +213,7 @@ bgp_encode_u32s(struct bgp_write_state *s UNUSED, eattr *a, byte *buf, uint size
   if (size < (4+len))
     return -1;
 
-  uint hdr = bgp_put_attr_hdr(buf, EA_ID(a->id), a->flags, len);
+  uint hdr = bgp_put_attr_hdr(buf, EA_BGP_ID(a->id), a->flags, len);
   put_u32s(buf + hdr, (u32 *) a->u.ptr->data, len / 4);
 
   return hdr + len;
@@ -219,7 +234,7 @@ bgp_put_attr(byte *buf, uint size, uint code, uint flags, const byte *data, uint
 static int
 bgp_encode_raw(struct bgp_write_state *s UNUSED, eattr *a, byte *buf, uint size)
 {
-  return bgp_put_attr(buf, size, EA_ID(a->id), a->flags, a->u.ptr->data, a->u.ptr->length);
+  return bgp_put_attr(buf, size, EA_BGP_ID(a->id), a->flags, a->u.ptr->data, a->u.ptr->length);
 }
 
 
@@ -359,7 +374,7 @@ bgp_aigp_set_metric(struct linpool *pool, const struct adata *ad, u64 metric)
 int
 bgp_total_aigp_metric_(rte *e, u64 *metric, const struct adata **ad)
 {
-  eattr *a = ea_find(e->attrs->eattrs, EA_CODE(PROTOCOL_BGP, BA_AIGP));
+  eattr *a = ea_find(e->attrs->eattrs, BGP_EA_ID(BA_AIGP));
   if (!a)
     return 0;
 
@@ -993,11 +1008,30 @@ bgp_format_mpls_label_stack(const eattr *a, byte *buf, uint size)
   pos[lnum ? -1 : 0] = 0;
 }
 
+static inline void
+bgp_export_unknown(struct bgp_export_state *s UNUSED, eattr *a)
+{
+  if (!(a->flags & BAF_TRANSITIVE))
+    UNSET(a);
+
+  a->flags |= BAF_PARTIAL;
+}
+
 static inline void
 bgp_decode_unknown(struct bgp_parse_state *s UNUSED, uint code, uint flags, byte *data, uint len, ea_list **to)
 {
+  if (!(flags & BAF_OPTIONAL))
+    WITHDRAW("Unknown attribute (code %u) - conflicting flags (%02x)", code, flags);
+
   /* Cannot use bgp_set_attr_data() as it works on known attributes only */
-  ea_set_attr_data(to, EA_CODE(PROTOCOL_BGP, code), flags, T_OPAQUE, data, len);
+  ea_set_attr_data(to, &bgp_attr_table[code].class, flags, data, len);
+}
+
+static inline void
+bgp_format_unknown(const eattr *a, byte *buf, uint size)
+{
+  if (a->flags & BAF_TRANSITIVE)
+    bsnprintf(buf, size, "(transitive)");
 }
 
 
@@ -1005,9 +1039,9 @@ bgp_decode_unknown(struct bgp_parse_state *s UNUSED, uint code, uint flags, byte
  *     Attribute table
  */
 
-static const struct bgp_attr_desc bgp_attr_table[] = {
+static union bgp_attr_desc bgp_attr_table[BGP_ATTR_MAX] = {
   [BA_ORIGIN] = {
-    .name = "origin",
+    .name = "bgp_origin",
     .type = T_ENUM_BGP_ORIGIN,
     .flags = BAF_TRANSITIVE,
     .export = bgp_export_origin,
@@ -1016,14 +1050,14 @@ static const struct bgp_attr_desc bgp_attr_table[] = {
     .format = bgp_format_origin,
   },
   [BA_AS_PATH] = {
-    .name = "as_path",
+    .name = "bgp_path",
     .type = T_PATH,
     .flags = BAF_TRANSITIVE,
     .encode = bgp_encode_as_path,
     .decode = bgp_decode_as_path,
   },
   [BA_NEXT_HOP] = {
-    .name = "next_hop",
+    .name = "bgp_next_hop",
     .type = T_IP,
     .flags = BAF_TRANSITIVE,
     .encode = bgp_encode_next_hop,
@@ -1031,14 +1065,14 @@ static const struct bgp_attr_desc bgp_attr_table[] = {
     .format = bgp_format_next_hop,
   },
   [BA_MULTI_EXIT_DISC] = {
-    .name = "med",
+    .name = "bgp_med",
     .type = T_INT,
     .flags = BAF_OPTIONAL,
     .encode = bgp_encode_u32,
     .decode = bgp_decode_med,
   },
   [BA_LOCAL_PREF] = {
-    .name = "local_pref",
+    .name = "bgp_local_pref",
     .type = T_INT,
     .flags = BAF_TRANSITIVE,
     .export = bgp_export_local_pref,
@@ -1046,14 +1080,14 @@ static const struct bgp_attr_desc bgp_attr_table[] = {
     .decode = bgp_decode_local_pref,
   },
   [BA_ATOMIC_AGGR] = {
-    .name = "atomic_aggr",
+    .name = "bgp_atomic_aggr",
     .type = T_OPAQUE,
     .flags = BAF_TRANSITIVE,
     .encode = bgp_encode_raw,
     .decode = bgp_decode_atomic_aggr,
   },
   [BA_AGGREGATOR] = {
-    .name = "aggregator",
+    .name = "bgp_aggregator",
     .type = T_OPAQUE,
     .flags = BAF_OPTIONAL | BAF_TRANSITIVE,
     .encode = bgp_encode_aggregator,
@@ -1061,7 +1095,7 @@ static const struct bgp_attr_desc bgp_attr_table[] = {
     .format = bgp_format_aggregator,
   },
   [BA_COMMUNITY] = {
-    .name = "community",
+    .name = "bgp_community",
     .type = T_CLIST,
     .flags = BAF_OPTIONAL | BAF_TRANSITIVE,
     .export = bgp_export_community,
@@ -1069,7 +1103,7 @@ static const struct bgp_attr_desc bgp_attr_table[] = {
     .decode = bgp_decode_community,
   },
   [BA_ORIGINATOR_ID] = {
-    .name = "originator_id",
+    .name = "bgp_originator_id",
     .type = T_QUAD,
     .flags = BAF_OPTIONAL,
     .export = bgp_export_originator_id,
@@ -1077,7 +1111,7 @@ static const struct bgp_attr_desc bgp_attr_table[] = {
     .decode = bgp_decode_originator_id,
   },
   [BA_CLUSTER_LIST] = {
-    .name = "cluster_list",
+    .name = "bgp_cluster_list",
     .type = T_CLIST,
     .flags = BAF_OPTIONAL,
     .export = bgp_export_cluster_list,
@@ -1086,19 +1120,19 @@ static const struct bgp_attr_desc bgp_attr_table[] = {
     .format = bgp_format_cluster_list,
   },
   [BA_MP_REACH_NLRI] = {
-    .name = "mp_reach_nlri",
+    .name = "bgp_mp_reach_nlri",
     .type = T_OPAQUE,
     .flags = BAF_OPTIONAL,
     .decode = bgp_decode_mp_reach_nlri,
   },
   [BA_MP_UNREACH_NLRI] = {
-    .name = "mp_unreach_nlri",
+    .name = "bgp_mp_unreach_nlri",
     .type = T_OPAQUE,
     .flags = BAF_OPTIONAL,
     .decode = bgp_decode_mp_unreach_nlri,
   },
   [BA_EXT_COMMUNITY] = {
-    .name = "ext_community",
+    .name = "bgp_ext_community",
     .type = T_ECLIST,
     .flags = BAF_OPTIONAL | BAF_TRANSITIVE,
     .export = bgp_export_ext_community,
@@ -1106,14 +1140,14 @@ static const struct bgp_attr_desc bgp_attr_table[] = {
     .decode = bgp_decode_ext_community,
   },
   [BA_AS4_PATH] = {
-    .name = "as4_path",
+    .name = "bgp_as4_path",
     .type = T_PATH,
     .flags = BAF_OPTIONAL | BAF_TRANSITIVE,
     .encode = bgp_encode_raw,
     .decode = bgp_decode_as4_path,
   },
   [BA_AS4_AGGREGATOR] = {
-    .name = "as4_aggregator",
+    .name = "bgp_as4_aggregator",
     .type = T_OPAQUE,
     .flags = BAF_OPTIONAL | BAF_TRANSITIVE,
     .encode = bgp_encode_raw,
@@ -1121,7 +1155,7 @@ static const struct bgp_attr_desc bgp_attr_table[] = {
     .format = bgp_format_aggregator,
   },
   [BA_AIGP] = {
-    .name = "aigp",
+    .name = "bgp_aigp",
     .type = T_OPAQUE,
     .flags = BAF_OPTIONAL | BAF_DECODE_FLAGS,
     .export = bgp_export_aigp,
@@ -1130,7 +1164,7 @@ static const struct bgp_attr_desc bgp_attr_table[] = {
     .format = bgp_format_aigp,
   },
   [BA_LARGE_COMMUNITY] = {
-    .name = "large_community",
+    .name = "bgp_large_community",
     .type = T_LCLIST,
     .flags = BAF_OPTIONAL | BAF_TRANSITIVE,
     .export = bgp_export_large_community,
@@ -1138,8 +1172,9 @@ static const struct bgp_attr_desc bgp_attr_table[] = {
     .decode = bgp_decode_large_community,
   },
   [BA_MPLS_LABEL_STACK] = {
-    .name = "mpls_label_stack",
+    .name = "bgp_mpls_label_stack",
     .type = T_CLIST,
+    .readonly = 1,
     .export = bgp_export_mpls_label_stack,
     .encode = bgp_encode_mpls_label_stack,
     .decode = bgp_decode_mpls_label_stack,
@@ -1147,12 +1182,32 @@ static const struct bgp_attr_desc bgp_attr_table[] = {
   },
 };
 
-static inline int
-bgp_attr_known(uint code)
+eattr *
+bgp_find_attr(ea_list *attrs, uint code)
 {
-  return (code < ARRAY_SIZE(bgp_attr_table)) && bgp_attr_table[code].name;
+  return ea_find(attrs, BGP_EA_ID(code));
 }
 
+void
+bgp_register_attrs(void)
+{
+  for (uint i=0; i<ARRAY_SIZE(bgp_attr_table); i++)
+  {
+    if (!bgp_attr_table[i].name)
+      bgp_attr_table[i] = (union bgp_attr_desc) {
+       .name = mb_sprintf(&root_pool, "bgp_unknown_0x%02x", i),
+       .type = T_OPAQUE,
+       .flags = BAF_OPTIONAL,
+       .readonly = 1,
+       .export = bgp_export_unknown,
+       .encode = bgp_encode_raw,
+       .decode = bgp_decode_unknown,
+       .format = bgp_format_unknown,
+      };
+
+    ea_register_init(&bgp_attr_table[i].class);
+  }
+}
 
 /*
  *     Attribute export
@@ -1161,38 +1216,24 @@ bgp_attr_known(uint code)
 static inline void
 bgp_export_attr(struct bgp_export_state *s, eattr *a, ea_list *to)
 {
-  if (EA_PROTO(a->id) != PROTOCOL_BGP)
+  const union bgp_attr_desc *desc = bgp_find_attr_desc(a);
+  if (!desc)
     return;
 
-  uint code = EA_ID(a->id);
-
-  if (bgp_attr_known(code))
-  {
-    const struct bgp_attr_desc *desc = &bgp_attr_table[code];
-
-    /* The flags might have been zero if the attr was added by filters */
-    a->flags = (a->flags & BAF_PARTIAL) | desc->flags;
-
-    /* Set partial bit if new opt-trans attribute is attached to non-local route */
-    if ((s->src != NULL) && (a->originated) &&
-       (a->flags & BAF_OPTIONAL) && (a->flags & BAF_TRANSITIVE))
-      a->flags |= BAF_PARTIAL;
+  /* The flags might have been zero if the attr was added locally */
+  a->flags = (a->flags & BAF_PARTIAL) | desc->flags;
 
-    /* Call specific hook */
-    CALL(desc->export, s, a);
+  /* Set partial bit if new opt-trans attribute is attached to non-local route */
+  if ((s->src != NULL) && (a->originated) &&
+      (a->flags & BAF_OPTIONAL) && (a->flags & BAF_TRANSITIVE))
+    a->flags |= BAF_PARTIAL;
 
-    /* Attribute might become undefined in hook */
-    if (a->undef)
-      return;
-  }
-  else
-  {
-    /* Don't re-export unknown non-transitive attributes */
-    if (!(a->flags & BAF_TRANSITIVE))
-      return;
+  /* Call specific hook */
+  CALL(desc->export, s, a);
 
-    a->flags |= BAF_PARTIAL;
-  }
+  /* Attribute might become undefined in hook */
+  if (a->undef)
+    return;
 
   /* Append updated attribute */
   to->attrs[to->count++] = *a;
@@ -1240,14 +1281,9 @@ bgp_export_attrs(struct bgp_export_state *s, const ea_list *a)
 static inline int
 bgp_encode_attr(struct bgp_write_state *s, eattr *a, byte *buf, uint size)
 {
-  ASSERT(EA_PROTO(a->id) == PROTOCOL_BGP);
-
-  uint code = EA_ID(a->id);
-
-  if (bgp_attr_known(code))
-    return bgp_attr_table[code].encode(s, a, buf, size);
-  else
-    return bgp_encode_raw(s, a, buf, size);
+  const union bgp_attr_desc *desc = bgp_find_attr_desc(a);
+  ASSERT_DIE(desc);
+  return desc->encode(s, a, buf, size);
 }
 
 /**
@@ -1312,7 +1348,7 @@ bgp_cluster_list_loopy(struct bgp_proto *p, ea_list *attrs)
 }
 
 static inline void
-bgp_decode_attr(struct bgp_parse_state *s, uint code, uint flags, byte *data, uint len, ea_list **to)
+bgp_decode_attr(struct bgp_parse_state *s, byte code, byte flags, byte *data, uint len, ea_list **to)
 {
   /* Handle duplicate attributes; RFC 7606 3 (g) */
   if (BIT32_TEST(s->attrs_seen, code))
@@ -1324,24 +1360,15 @@ bgp_decode_attr(struct bgp_parse_state *s, uint code, uint flags, byte *data, ui
   }
   BIT32_SET(s->attrs_seen, code);
 
-  if (bgp_attr_known(code))
-  {
-    const struct bgp_attr_desc *desc = &bgp_attr_table[code];
+  ASSERT_DIE(bgp_attr_table[code].id);
+  const union bgp_attr_desc *desc = &bgp_attr_table[code];
 
-    /* Handle conflicting flags; RFC 7606 3 (c) */
-    if (((flags ^ desc->flags) & (BAF_OPTIONAL | BAF_TRANSITIVE)) &&
-       !(desc->flags & BAF_DECODE_FLAGS))
-      WITHDRAW("Malformed %s attribute - conflicting flags (%02x)", desc->name, flags);
+  /* Handle conflicting flags; RFC 7606 3 (c) */
+  if (((flags ^ desc->flags) & (BAF_OPTIONAL | BAF_TRANSITIVE)) &&
+      !(desc->flags & BAF_DECODE_FLAGS))
+    WITHDRAW("Malformed %s attribute - conflicting flags (%02x, expected %02x)", desc->name, flags, desc->flags);
 
-    desc->decode(s, code, flags, data, len, to);
-  }
-  else /* Unknown attribute */
-  {
-    if (!(flags & BAF_OPTIONAL))
-      WITHDRAW("Unknown attribute (code %u) - conflicting flags (%02x)", code, flags);
-
-    bgp_decode_unknown(s, code, flags, data, len, to);
-  }
+  desc->decode(s, code, flags, data, len, to);
 }
 
 /**
@@ -1359,7 +1386,8 @@ bgp_decode_attrs(struct bgp_parse_state *s, byte *data, uint len)
 {
   struct bgp_proto *p = s->proto;
   ea_list *attrs = NULL;
-  uint code, flags, alen;
+  uint alen;
+  byte code, flags;
   byte *pos = data;
 
   /* Parse the attributes */
@@ -1703,7 +1731,7 @@ bgp_preexport(struct proto *P, rte *e)
   /* Handle well-known communities, RFC 1997 */
   struct eattr *c;
   if (p->cf->interpret_communities &&
-      (c = ea_find(e->attrs->eattrs, EA_CODE(PROTOCOL_BGP, BA_COMMUNITY))))
+      (c = ea_find(e->attrs->eattrs, BGP_EA_ID(BA_COMMUNITY))))
   {
     const struct adata *d = c->u.ptr;
 
@@ -1880,7 +1908,7 @@ bgp_rt_notify(struct proto *P, struct channel *C, net *n, rte *new, rte *old)
 static inline u32
 bgp_get_neighbor(rte *r)
 {
-  eattr *e = ea_find(r->attrs->eattrs, EA_CODE(PROTOCOL_BGP, BA_AS_PATH));
+  eattr *e = ea_find(r->attrs->eattrs, BGP_EA_ID(BA_AS_PATH));
   u32 as;
 
   if (e && as_path_get_first_regular(e->u.ptr, &as))
@@ -1901,7 +1929,7 @@ rte_stale(rte *r)
     return 0;
 
   /* If staleness is unknown, compute and cache it */
-  eattr *a = ea_find(r->attrs->eattrs, EA_CODE(PROTOCOL_BGP, BA_COMMUNITY));
+  eattr *a = ea_find(r->attrs->eattrs, BGP_EA_ID(BA_COMMUNITY));
   if (a && int_set_contains(a->u.ptr, BGP_COMM_LLGR_STALE))
   {
     r->pflags |= BGP_REF_STALE;
@@ -1947,8 +1975,8 @@ bgp_rte_better(rte *new, rte *old)
     return 1;
 
  /* Start with local preferences */
-  x = ea_find(new->attrs->eattrs, EA_CODE(PROTOCOL_BGP, BA_LOCAL_PREF));
-  y = ea_find(old->attrs->eattrs, EA_CODE(PROTOCOL_BGP, BA_LOCAL_PREF));
+  x = ea_find(new->attrs->eattrs, BGP_EA_ID(BA_LOCAL_PREF));
+  y = ea_find(old->attrs->eattrs, BGP_EA_ID(BA_LOCAL_PREF));
   n = x ? x->u.data : new_bgp->cf->default_local_pref;
   o = y ? y->u.data : old_bgp->cf->default_local_pref;
   if (n > o)
@@ -1967,8 +1995,8 @@ bgp_rte_better(rte *new, rte *old)
   /* RFC 4271 9.1.2.2. a)  Use AS path lengths */
   if (new_bgp->cf->compare_path_lengths || old_bgp->cf->compare_path_lengths)
   {
-    x = ea_find(new->attrs->eattrs, EA_CODE(PROTOCOL_BGP, BA_AS_PATH));
-    y = ea_find(old->attrs->eattrs, EA_CODE(PROTOCOL_BGP, BA_AS_PATH));
+    x = ea_find(new->attrs->eattrs, BGP_EA_ID(BA_AS_PATH));
+    y = ea_find(old->attrs->eattrs, BGP_EA_ID(BA_AS_PATH));
     n = x ? as_path_getlen(x->u.ptr) : AS_PATH_MAXLEN;
     o = y ? as_path_getlen(y->u.ptr) : AS_PATH_MAXLEN;
     if (n < o)
@@ -1978,8 +2006,8 @@ bgp_rte_better(rte *new, rte *old)
   }
 
   /* RFC 4271 9.1.2.2. b) Use origins */
-  x = ea_find(new->attrs->eattrs, EA_CODE(PROTOCOL_BGP, BA_ORIGIN));
-  y = ea_find(old->attrs->eattrs, EA_CODE(PROTOCOL_BGP, BA_ORIGIN));
+  x = ea_find(new->attrs->eattrs, BGP_EA_ID(BA_ORIGIN));
+  y = ea_find(old->attrs->eattrs, BGP_EA_ID(BA_ORIGIN));
   n = x ? x->u.data : ORIGIN_INCOMPLETE;
   o = y ? y->u.data : ORIGIN_INCOMPLETE;
   if (n < o)
@@ -2001,8 +2029,8 @@ bgp_rte_better(rte *new, rte *old)
   if (new_bgp->cf->med_metric || old_bgp->cf->med_metric ||
       (bgp_get_neighbor(new) == bgp_get_neighbor(old)))
   {
-    x = ea_find(new->attrs->eattrs, EA_CODE(PROTOCOL_BGP, BA_MULTI_EXIT_DISC));
-    y = ea_find(old->attrs->eattrs, EA_CODE(PROTOCOL_BGP, BA_MULTI_EXIT_DISC));
+    x = ea_find(new->attrs->eattrs, BGP_EA_ID(BA_MULTI_EXIT_DISC));
+    y = ea_find(old->attrs->eattrs, BGP_EA_ID(BA_MULTI_EXIT_DISC));
     n = x ? x->u.data : new_bgp->cf->default_med;
     o = y ? y->u.data : old_bgp->cf->default_med;
     if (n < o)
@@ -2027,8 +2055,8 @@ bgp_rte_better(rte *new, rte *old)
 
   /* RFC 4271 9.1.2.2. f) Compare BGP identifiers */
   /* RFC 4456 9. a) Use ORIGINATOR_ID instead of local neighbor ID */
-  x = ea_find(new->attrs->eattrs, EA_CODE(PROTOCOL_BGP, BA_ORIGINATOR_ID));
-  y = ea_find(old->attrs->eattrs, EA_CODE(PROTOCOL_BGP, BA_ORIGINATOR_ID));
+  x = ea_find(new->attrs->eattrs, BGP_EA_ID(BA_ORIGINATOR_ID));
+  y = ea_find(old->attrs->eattrs, BGP_EA_ID(BA_ORIGINATOR_ID));
   n = x ? x->u.data : new_bgp->remote_id;
   o = y ? y->u.data : old_bgp->remote_id;
 
@@ -2045,8 +2073,8 @@ bgp_rte_better(rte *new, rte *old)
     return 0;
 
   /* RFC 4456 9. b) Compare cluster list lengths */
-  x = ea_find(new->attrs->eattrs, EA_CODE(PROTOCOL_BGP, BA_CLUSTER_LIST));
-  y = ea_find(old->attrs->eattrs, EA_CODE(PROTOCOL_BGP, BA_CLUSTER_LIST));
+  x = ea_find(new->attrs->eattrs, BGP_EA_ID(BA_CLUSTER_LIST));
+  y = ea_find(old->attrs->eattrs, BGP_EA_ID(BA_CLUSTER_LIST));
   n = x ? int_set_get_size(x->u.ptr) : 0;
   o = y ? int_set_get_size(y->u.ptr) : 0;
   if (n < o)
@@ -2080,8 +2108,8 @@ bgp_rte_mergable(rte *pri, rte *sec)
     return 0;
 
   /* Start with local preferences */
-  x = ea_find(pri->attrs->eattrs, EA_CODE(PROTOCOL_BGP, BA_LOCAL_PREF));
-  y = ea_find(sec->attrs->eattrs, EA_CODE(PROTOCOL_BGP, BA_LOCAL_PREF));
+  x = ea_find(pri->attrs->eattrs, BGP_EA_ID(BA_LOCAL_PREF));
+  y = ea_find(sec->attrs->eattrs, BGP_EA_ID(BA_LOCAL_PREF));
   p = x ? x->u.data : pri_bgp->cf->default_local_pref;
   s = y ? y->u.data : sec_bgp->cf->default_local_pref;
   if (p != s)
@@ -2090,8 +2118,8 @@ bgp_rte_mergable(rte *pri, rte *sec)
   /* RFC 4271 9.1.2.2. a)  Use AS path lengths */
   if (pri_bgp->cf->compare_path_lengths || sec_bgp->cf->compare_path_lengths)
   {
-    x = ea_find(pri->attrs->eattrs, EA_CODE(PROTOCOL_BGP, BA_AS_PATH));
-    y = ea_find(sec->attrs->eattrs, EA_CODE(PROTOCOL_BGP, BA_AS_PATH));
+    x = ea_find(pri->attrs->eattrs, BGP_EA_ID(BA_AS_PATH));
+    y = ea_find(sec->attrs->eattrs, BGP_EA_ID(BA_AS_PATH));
     p = x ? as_path_getlen(x->u.ptr) : AS_PATH_MAXLEN;
     s = y ? as_path_getlen(y->u.ptr) : AS_PATH_MAXLEN;
 
@@ -2103,8 +2131,8 @@ bgp_rte_mergable(rte *pri, rte *sec)
   }
 
   /* RFC 4271 9.1.2.2. b) Use origins */
-  x = ea_find(pri->attrs->eattrs, EA_CODE(PROTOCOL_BGP, BA_ORIGIN));
-  y = ea_find(sec->attrs->eattrs, EA_CODE(PROTOCOL_BGP, BA_ORIGIN));
+  x = ea_find(pri->attrs->eattrs, BGP_EA_ID(BA_ORIGIN));
+  y = ea_find(sec->attrs->eattrs, BGP_EA_ID(BA_ORIGIN));
   p = x ? x->u.data : ORIGIN_INCOMPLETE;
   s = y ? y->u.data : ORIGIN_INCOMPLETE;
   if (p != s)
@@ -2114,8 +2142,8 @@ bgp_rte_mergable(rte *pri, rte *sec)
   if (pri_bgp->cf->med_metric || sec_bgp->cf->med_metric ||
       (bgp_get_neighbor(pri) == bgp_get_neighbor(sec)))
   {
-    x = ea_find(pri->attrs->eattrs, EA_CODE(PROTOCOL_BGP, BA_MULTI_EXIT_DISC));
-    y = ea_find(sec->attrs->eattrs, EA_CODE(PROTOCOL_BGP, BA_MULTI_EXIT_DISC));
+    x = ea_find(pri->attrs->eattrs, BGP_EA_ID(BA_MULTI_EXIT_DISC));
+    y = ea_find(sec->attrs->eattrs, BGP_EA_ID(BA_MULTI_EXIT_DISC));
     p = x ? x->u.data : pri_bgp->cf->default_med;
     s = y ? y->u.data : sec_bgp->cf->default_med;
     if (p != s)
@@ -2281,7 +2309,7 @@ bgp_rte_recalculate(rtable *table, net *net, rte *new, rte *old, rte *old_best)
 struct rte *
 bgp_rte_modify_stale(struct rte *r, struct linpool *pool)
 {
-  eattr *a = ea_find(r->attrs->eattrs, EA_CODE(PROTOCOL_BGP, BA_COMMUNITY));
+  eattr *a = ea_find(r->attrs->eattrs, BGP_EA_ID(BA_COMMUNITY));
   const struct adata *ad = a ? a->u.ptr : NULL;
   uint flags = a ? a->flags : BAF_PARTIAL;
 
@@ -2346,37 +2374,11 @@ bgp_process_as4_attrs(ea_list **attrs, struct linpool *pool)
   }
 }
 
-int
-bgp_get_attr(const eattr *a, byte *buf, int buflen)
-{
-  uint i = EA_ID(a->id);
-  const struct bgp_attr_desc *d;
-  int len;
-
-  if (bgp_attr_known(i))
-  {
-    d = &bgp_attr_table[i];
-    len = bsprintf(buf, "%s", d->name);
-    buf += len;
-    if (d->format)
-    {
-      *buf++ = ':';
-      *buf++ = ' ';
-      d->format(a, buf, buflen - len - 2);
-      return GA_FULL;
-    }
-    return GA_NAME;
-  }
-
-  bsprintf(buf, "%02x%s", i, (a->flags & BAF_TRANSITIVE) ? " [t]" : "");
-  return GA_NAME;
-}
-
 void
 bgp_get_route_info(rte *e, byte *buf)
 {
-  eattr *p = ea_find(e->attrs->eattrs, EA_CODE(PROTOCOL_BGP, BA_AS_PATH));
-  eattr *o = ea_find(e->attrs->eattrs, EA_CODE(PROTOCOL_BGP, BA_ORIGIN));
+  eattr *p = ea_find(e->attrs->eattrs, BGP_EA_ID(BA_AS_PATH));
+  eattr *o = ea_find(e->attrs->eattrs, BGP_EA_ID(BA_ORIGIN));
   u32 origas;
 
   buf += bsprintf(buf, " (%d", e->attrs->pref);
index aec78a45208aaa69b407ff946dab7e9cff525958..bd4c68b7a5daf011f4d282bb83e320f13206c735 100644 (file)
@@ -2576,7 +2576,6 @@ struct channel_class channel_bgp = {
 struct protocol proto_bgp = {
   .name =              "BGP",
   .template =          "bgp%d",
-  .class =             PROTOCOL_BGP,
   .preference =        DEF_PREF_BGP,
   .channel_mask =      NB_IP | NB_VPN | NB_FLOW,
   .proto_size =                sizeof(struct bgp_proto),
@@ -2588,7 +2587,6 @@ struct protocol proto_bgp = {
   .reconfigure =       bgp_reconfigure,
   .copy_config =       bgp_copy_config,
   .get_status =        bgp_get_status,
-  .get_attr =          bgp_get_attr,
   .get_route_info =    bgp_get_route_info,
   .show_proto_info =   bgp_show_proto_info
 };
@@ -2596,4 +2594,5 @@ struct protocol proto_bgp = {
 void bgp_build(void)
 {
   proto_build(&proto_bgp);
+  bgp_register_attrs();
 }
index de5bd836a8bb0b9c05f998f5ab75059b324afe2a..e04e3bd079a340d38b8be327eeba550150a82f2f 100644 (file)
@@ -538,17 +538,13 @@ rte_resolvable(rte *rt)
 
 /* attrs.c */
 
-static inline eattr *
-bgp_find_attr(ea_list *attrs, uint code)
-{
-  return ea_find(attrs, EA_CODE(PROTOCOL_BGP, code));
-}
+eattr *
+bgp_find_attr(ea_list *attrs, uint code);
 
 void bgp_set_attr_u32(ea_list **to, uint code, uint flags, u32 val);
 void bgp_set_attr_ptr(ea_list **to, uint code, uint flags, const struct adata *ad);
 void bgp_set_attr_data(ea_list **to, uint code, uint flags, void *data, uint len);
-
-#define bgp_unset_attr(to, code) ea_unset_attr(to, 0, code)
+void bgp_unset_attr(ea_list **to, uint code);
 
 int bgp_encode_mp_reach_mrt(struct bgp_write_state *s, eattr *a, byte *buf, uint size);
 
@@ -573,7 +569,6 @@ struct rte *bgp_rte_modify_stale(struct rte *r, struct linpool *pool);
 u32 bgp_rte_igp_metric(struct rte *);
 void bgp_rt_notify(struct proto *P, struct channel *C, net *n, rte *new, rte *old);
 int bgp_preexport(struct proto *, struct rte *);
-int bgp_get_attr(const struct eattr *e, byte *buf, int buflen);
 void bgp_get_route_info(struct rte *, byte *buf);
 int bgp_total_aigp_metric_(rte *e, u64 *metric, const struct adata **ad);
 
@@ -590,6 +585,8 @@ bgp_total_aigp_metric(rte *r)
   return metric;
 }
 
+void bgp_register_attrs(void);
+
 
 /* packets.c */
 
@@ -626,26 +623,31 @@ void bgp_update_next_hop(struct bgp_export_state *s, eattr *a, ea_list **to);
 
 #define BAF_DECODE_FLAGS       0x0100  /* Private flag - attribute flags are handled by the decode hook */
 
-#define BA_ORIGIN              0x01    /* RFC 4271 */          /* WM */
-#define BA_AS_PATH             0x02                            /* WM */
-#define BA_NEXT_HOP            0x03                            /* WM */
-#define BA_MULTI_EXIT_DISC     0x04                            /* ON */
-#define BA_LOCAL_PREF          0x05                            /* WD */
-#define BA_ATOMIC_AGGR         0x06                            /* WD */
-#define BA_AGGREGATOR          0x07                            /* OT */
-#define BA_COMMUNITY           0x08    /* RFC 1997 */          /* OT */
-#define BA_ORIGINATOR_ID       0x09    /* RFC 4456 */          /* ON */
-#define BA_CLUSTER_LIST                0x0a    /* RFC 4456 */          /* ON */
-#define BA_MP_REACH_NLRI       0x0e    /* RFC 4760 */
-#define BA_MP_UNREACH_NLRI     0x0f    /* RFC 4760 */
-#define BA_EXT_COMMUNITY       0x10    /* RFC 4360 */
-#define BA_AS4_PATH             0x11   /* RFC 6793 */
-#define BA_AS4_AGGREGATOR       0x12   /* RFC 6793 */
-#define BA_AIGP                        0x1a    /* RFC 7311 */
-#define BA_LARGE_COMMUNITY     0x20    /* RFC 8092 */
+enum bgp_attr_id {
+  BA_ORIGIN            = 0x01, /* RFC 4271 */          /* WM */
+  BA_AS_PATH           = 0x02,                         /* WM */
+  BA_NEXT_HOP          = 0x03,                         /* WM */
+  BA_MULTI_EXIT_DISC   = 0x04,                         /* ON */
+  BA_LOCAL_PREF                = 0x05,                         /* WD */
+  BA_ATOMIC_AGGR       = 0x06,                         /* WD */
+  BA_AGGREGATOR                = 0x07,                         /* OT */
+  BA_COMMUNITY         = 0x08, /* RFC 1997 */          /* OT */
+  BA_ORIGINATOR_ID     = 0x09, /* RFC 4456 */          /* ON */
+  BA_CLUSTER_LIST      = 0x0a, /* RFC 4456 */          /* ON */
+  BA_MP_REACH_NLRI     = 0x0e, /* RFC 4760 */
+  BA_MP_UNREACH_NLRI   = 0x0f, /* RFC 4760 */
+  BA_EXT_COMMUNITY     = 0x10, /* RFC 4360 */
+  BA_AS4_PATH          = 0x11, /* RFC 6793 */
+  BA_AS4_AGGREGATOR    = 0x12, /* RFC 6793 */
+  BA_AIGP              = 0x1a, /* RFC 7311 */
+  BA_LARGE_COMMUNITY   = 0x20, /* RFC 8092 */
 
 /* Bird's private internal BGP attributes */
-#define BA_MPLS_LABEL_STACK    0xfe    /* MPLS label stack transfer attribute */
+  BA_MPLS_LABEL_STACK  = 0x100, /* MPLS label stack transfer attribute */
+
+/* Maximum */
+  BGP_ATTR_MAX,
+};
 
 /* BGP connection states */
 
index 6a78f79ba03b3ece904c445940b4aabf3005a6a3..db261bbbe1f58fd15db7e40963d1beceedaa4984 100644 (file)
@@ -316,36 +316,6 @@ bgp_channel_end:
 
 bgp_proto_channel: bgp_channel_start bgp_channel_opt_list bgp_channel_end;
 
-
-dynamic_attr: BGP_ORIGIN
-       { $$ = f_new_dynamic_attr(T_ENUM_BGP_ORIGIN, EA_CODE(PROTOCOL_BGP, BA_ORIGIN)); } ;
-dynamic_attr: BGP_PATH
-       { $$ = f_new_dynamic_attr(T_PATH, EA_CODE(PROTOCOL_BGP, BA_AS_PATH)); } ;
-dynamic_attr: BGP_NEXT_HOP
-       { $$ = f_new_dynamic_attr(T_IP, EA_CODE(PROTOCOL_BGP, BA_NEXT_HOP)); } ;
-dynamic_attr: BGP_MED
-       { $$ = f_new_dynamic_attr(T_INT, EA_CODE(PROTOCOL_BGP, BA_MULTI_EXIT_DISC)); } ;
-dynamic_attr: BGP_LOCAL_PREF
-       { $$ = f_new_dynamic_attr(T_INT, EA_CODE(PROTOCOL_BGP, BA_LOCAL_PREF)); } ;
-dynamic_attr: BGP_ATOMIC_AGGR
-       { $$ = f_new_dynamic_attr(T_OPAQUE, EA_CODE(PROTOCOL_BGP, BA_ATOMIC_AGGR)); } ;
-dynamic_attr: BGP_AGGREGATOR
-       { $$ = f_new_dynamic_attr(T_OPAQUE, EA_CODE(PROTOCOL_BGP, BA_AGGREGATOR)); } ;
-dynamic_attr: BGP_COMMUNITY
-       { $$ = f_new_dynamic_attr(T_CLIST, EA_CODE(PROTOCOL_BGP, BA_COMMUNITY)); } ;
-dynamic_attr: BGP_ORIGINATOR_ID
-       { $$ = f_new_dynamic_attr(T_QUAD, EA_CODE(PROTOCOL_BGP, BA_ORIGINATOR_ID)); } ;
-dynamic_attr: BGP_CLUSTER_LIST
-       { $$ = f_new_dynamic_attr(T_CLIST, EA_CODE(PROTOCOL_BGP, BA_CLUSTER_LIST)); } ;
-dynamic_attr: BGP_EXT_COMMUNITY
-       { $$ = f_new_dynamic_attr(T_ECLIST, EA_CODE(PROTOCOL_BGP, BA_EXT_COMMUNITY)); } ;
-dynamic_attr: BGP_AIGP
-       { $$ = f_new_dynamic_attr(T_OPAQUE, EA_CODE(PROTOCOL_BGP, BA_AIGP)); } ;
-dynamic_attr: BGP_LARGE_COMMUNITY
-       { $$ = f_new_dynamic_attr(T_LCLIST, EA_CODE(PROTOCOL_BGP, BA_LARGE_COMMUNITY)); } ;
-
-
-
 CF_ENUM(T_ENUM_BGP_ORIGIN, ORIGIN_, IGP, EGP, INCOMPLETE)
 
 CF_CODE
index c595f2987e36cedfd1694024503d5fffb5aeab48..e45f7cb7bd51a816088321a3454db617a578ee00 100644 (file)
@@ -907,7 +907,6 @@ mrt_copy_config(struct proto_config *dest UNUSED, struct proto_config *src UNUSE
 struct protocol proto_mrt = {
   .name =              "MRT",
   .template =          "mrt%d",
-  .class =             PROTOCOL_MRT,
   .proto_size =                sizeof(struct mrt_proto),
   .config_size =       sizeof(struct mrt_config),
   .init =              mrt_init,
index a8972d2c48af7cd7b5eeeed6843d16488ed3dafc..136e1dcb057182f30d0593f9af0e3aa5a90bd80e 100644 (file)
@@ -505,11 +505,6 @@ ospf_iface:
   ospf_iface_start ospf_iface_patt_list ospf_iface_opt_list { ospf_iface_finish(); }
  ;
 
-dynamic_attr: OSPF_METRIC1 { $$ = f_new_dynamic_attr(T_INT, EA_OSPF_METRIC1); } ;
-dynamic_attr: OSPF_METRIC2 { $$ = f_new_dynamic_attr(T_INT, EA_OSPF_METRIC2); } ;
-dynamic_attr: OSPF_TAG { $$ = f_new_dynamic_attr(T_INT, EA_OSPF_TAG); } ;
-dynamic_attr: OSPF_ROUTER_ID { $$ = f_new_dynamic_attr(T_QUAD, EA_OSPF_ROUTER_ID); } ;
-
 CF_CLI_HELP(SHOW OSPF, ..., [[Show information about OSPF protocol]]);
 CF_CLI(SHOW OSPF, optproto, [<name>], [[Show information about OSPF protocol]])
 { PROTO_WALK_CMD($3, &proto_ospf, p) ospf_sh(p); };
index d8bcc8383b72db0667fca0e78deb4b19a69330fc..66d0eba67612afed2587ff4f3dc1199e342217d9 100644 (file)
 
 #include <stdlib.h>
 #include "ospf.h"
+#include "lib/macro.h"
 
 static int ospf_preexport(struct proto *P, rte *new);
 static void ospf_reload_routes(struct channel *C);
@@ -386,7 +387,7 @@ ospf_init(struct proto_config *CF)
 static int
 ospf_rte_better(struct rte *new, struct rte *old)
 {
-  u32 new_metric1 = ea_get_int(new->attrs->eattrs, EA_OSPF_METRIC1, LSINFINITY);
+  u32 new_metric1 = ea_get_int(new->attrs->eattrs, &ea_ospf_metric1, LSINFINITY);
 
   if (new_metric1 == LSINFINITY)
     return 0;
@@ -396,13 +397,13 @@ ospf_rte_better(struct rte *new, struct rte *old)
 
   if(new->attrs->source == RTS_OSPF_EXT2)
   {
-    u32 old_metric2 = ea_get_int(old->attrs->eattrs, EA_OSPF_METRIC2, LSINFINITY);
-    u32 new_metric2 = ea_get_int(new->attrs->eattrs, EA_OSPF_METRIC2, LSINFINITY);
+    u32 old_metric2 = ea_get_int(old->attrs->eattrs, &ea_ospf_metric2, LSINFINITY);
+    u32 new_metric2 = ea_get_int(new->attrs->eattrs, &ea_ospf_metric2, LSINFINITY);
     if(new_metric2 < old_metric2) return 1;
     if(new_metric2 > old_metric2) return 0;
   }
 
-  u32 old_metric1 = ea_get_int(old->attrs->eattrs, EA_OSPF_METRIC1, LSINFINITY);
+  u32 old_metric1 = ea_get_int(old->attrs->eattrs, &ea_ospf_metric1, LSINFINITY);
   if (new_metric1 < old_metric1)
     return 1;
 
@@ -415,7 +416,7 @@ ospf_rte_igp_metric(struct rte *rt)
   if (rt->attrs->source == RTS_OSPF_EXT2)
     return IGP_METRIC_UNKNOWN;
 
-  return ea_get_int(rt->attrs->eattrs, EA_OSPF_METRIC1, LSINFINITY);
+  return ea_get_int(rt->attrs->eattrs, &ea_ospf_metric1, LSINFINITY);
 }
 
 void
@@ -587,42 +588,26 @@ ospf_get_route_info(rte * rte, byte * buf)
   }
 
   buf += bsprintf(buf, " %s", type);
-  buf += bsprintf(buf, " (%d/%d", rte->attrs->pref, ea_get_int(rte->attrs->eattrs, EA_OSPF_METRIC1, LSINFINITY));
+  buf += bsprintf(buf, " (%d/%d", rte->attrs->pref, ea_get_int(rte->attrs->eattrs, &ea_ospf_metric1, LSINFINITY));
   if (rte->attrs->source == RTS_OSPF_EXT2)
-    buf += bsprintf(buf, "/%d", ea_get_int(rte->attrs->eattrs, EA_OSPF_METRIC2, LSINFINITY));
+    buf += bsprintf(buf, "/%d", ea_get_int(rte->attrs->eattrs, &ea_ospf_metric2, LSINFINITY));
   buf += bsprintf(buf, ")");
   if (rte->attrs->source == RTS_OSPF_EXT1 || rte->attrs->source == RTS_OSPF_EXT2)
   {
-    eattr *ea = ea_find(rte->attrs->eattrs, EA_OSPF_TAG);
+    eattr *ea = ea_find(rte->attrs->eattrs, &ea_ospf_tag);
     if (ea && (ea->u.data > 0))
       buf += bsprintf(buf, " [%x]", ea->u.data);
   }
   
-  eattr *ea = ea_find(rte->attrs->eattrs, EA_OSPF_ROUTER_ID);
+  eattr *ea = ea_find(rte->attrs->eattrs, &ea_ospf_router_id);
   if (ea)
     buf += bsprintf(buf, " [%R]", ea->u.data);
 }
 
-static int
-ospf_get_attr(const eattr * a, byte * buf, int buflen UNUSED)
+static void
+ospf_tag_format(const eattr * a, byte * buf, uint buflen)
 {
-  switch (a->id)
-  {
-  case EA_OSPF_METRIC1:
-    bsprintf(buf, "metric1");
-    return GA_NAME;
-  case EA_OSPF_METRIC2:
-    bsprintf(buf, "metric2");
-    return GA_NAME;
-  case EA_OSPF_TAG:
-    bsprintf(buf, "tag: 0x%08x", a->u.data);
-    return GA_FULL;
-  case EA_OSPF_ROUTER_ID:
-    bsprintf(buf, "router_id");
-    return GA_NAME;
-  default:
-    return GA_UNKNOWN;
-  }
+  bsnprintf(buf, buflen, "0x%08x", a->u.data);
 }
 
 static void
@@ -1520,7 +1505,6 @@ ospf_sh_lsadb(struct lsadb_show_data *ld)
 struct protocol proto_ospf = {
   .name =              "OSPF",
   .template =          "ospf%d",
-  .class =             PROTOCOL_OSPF,
   .preference =                DEF_PREF_OSPF,
   .channel_mask =      NB_IP,
   .proto_size =                sizeof(struct ospf_proto),
@@ -1531,12 +1515,39 @@ struct protocol proto_ospf = {
   .shutdown =          ospf_shutdown,
   .reconfigure =       ospf_reconfigure,
   .get_status =                ospf_get_status,
-  .get_attr =          ospf_get_attr,
   .get_route_info =    ospf_get_route_info
 };
 
+struct ea_class ea_ospf_metric1 = {
+  .name = "ospf_metric1",
+  .type = T_INT,
+};
+
+struct ea_class ea_ospf_metric2 = {
+  .name = "ospf_metric2",
+  .type = T_INT,
+};
+
+struct ea_class ea_ospf_tag = {
+  .name = "ospf_tag",
+  .type = T_INT,
+  .format = ospf_tag_format,
+};
+
+struct ea_class ea_ospf_router_id = {
+  .name = "ospf_router_id",
+  .type = T_QUAD,
+};
+
 void
 ospf_build(void)
 {
   proto_build(&proto_ospf);
+
+  EA_REGISTER_ALL(
+      &ea_ospf_metric1,
+      &ea_ospf_metric2,
+      &ea_ospf_tag,
+      &ea_ospf_router_id
+  );
 }
index 3d0d57d9f145b3248a2f48e7303591aeeebf32e9..7bed5c85bd0ca3de36afff50e6cc5fd446ca3ffd 100644 (file)
@@ -939,12 +939,7 @@ struct lsadb_show_data {
   u32 router;          /* Advertising router, 0 -> all */
 };
 
-
-#define EA_OSPF_METRIC1        EA_CODE(PROTOCOL_OSPF, 0)
-#define EA_OSPF_METRIC2        EA_CODE(PROTOCOL_OSPF, 1)
-#define EA_OSPF_TAG    EA_CODE(PROTOCOL_OSPF, 2)
-#define EA_OSPF_ROUTER_ID EA_CODE(PROTOCOL_OSPF, 3)
-
+extern struct ea_class ea_ospf_metric1, ea_ospf_metric2, ea_ospf_tag, ea_ospf_router_id;
 
 /*
  * For regular networks, neighbor address must match network prefix.
index 5969b7c7c3961d0870f0958b35a925d771aa008b..55bad59993516f1bf7325b4e6063ee03b205e497 100644 (file)
@@ -2075,18 +2075,18 @@ again1:
        eattrs.l = (ea_list) {};
 
        eattrs.a[eattrs.l.count++] =
-         EA_LITERAL_EMBEDDED(EA_OSPF_METRIC1, T_INT, 0, nf->n.metric1);
+         EA_LITERAL_EMBEDDED(&ea_ospf_metric1, 0, nf->n.metric1);
 
        if (nf->n.type == RTS_OSPF_EXT2)
          eattrs.a[eattrs.l.count++] =
-           EA_LITERAL_EMBEDDED(EA_OSPF_METRIC2, T_INT, 0, nf->n.metric2);
+           EA_LITERAL_EMBEDDED(&ea_ospf_metric2, 0, nf->n.metric2);
 
        if ((nf->n.type == RTS_OSPF_EXT1) || (nf->n.type == RTS_OSPF_EXT2))
          eattrs.a[eattrs.l.count++] =
-           EA_LITERAL_EMBEDDED(EA_OSPF_TAG, T_INT, 0, nf->n.tag);
+           EA_LITERAL_EMBEDDED(&ea_ospf_tag, 0, nf->n.tag);
 
        eattrs.a[eattrs.l.count++] =
-         EA_LITERAL_EMBEDDED(EA_OSPF_ROUTER_ID, T_QUAD, 0, nf->n.rid);
+         EA_LITERAL_EMBEDDED(&ea_ospf_router_id, 0, nf->n.rid);
 
        a0.eattrs = &eattrs.l;
 
index 9fe682646c8406f6a8f3905d14eb41d698b2e51c..db423dc8e584db60e9c53eb57ee2382ba00f850c 100644 (file)
@@ -1338,8 +1338,8 @@ ospf_rt_notify(struct proto *P, struct channel *ch UNUSED, net *n, rte *new, rte
 
   /* Get route attributes */
   rta *a = new->attrs;
-  eattr *m1a = ea_find(a->eattrs, EA_OSPF_METRIC1);
-  eattr *m2a = ea_find(a->eattrs, EA_OSPF_METRIC2);
+  eattr *m1a = ea_find(a->eattrs, &ea_ospf_metric1);
+  eattr *m2a = ea_find(a->eattrs, &ea_ospf_metric2);
   uint m1 = m1a ? m1a->u.data : 0;
   uint m2 = m2a ? m2a->u.data : 10000;
 
@@ -1363,7 +1363,7 @@ ospf_rt_notify(struct proto *P, struct channel *ch UNUSED, net *n, rte *new, rte
 
   uint ebit = m2a || !m1a;
   uint metric = ebit ? m2 : m1;
-  uint tag = ea_get_int(a->eattrs, EA_OSPF_TAG, 0);
+  uint tag = ea_get_int(a->eattrs, &ea_ospf_tag, 0);
 
   ip_addr fwd = IPA_NONE;
   if ((a->dest == RTD_UNICAST) && use_gw_for_fwaddr(p, a->nh.gw, a->nh.iface))
index dde7e47321aea1bce0714b092e994a4170377caa..2978100b2b4c99064bf6289867dd03a3d70a21c2 100644 (file)
@@ -306,7 +306,6 @@ perf_copy_config(struct proto_config *dest UNUSED, struct proto_config *src UNUS
 struct protocol proto_perf = {
   .name =              "Perf",
   .template =          "perf%d",
-  .class =             PROTOCOL_PERF,
   .channel_mask =      NB_IP,
   .proto_size =                sizeof(struct perf_proto),
   .config_size =       sizeof(struct perf_config),
index 483ece678445a3da546f7f944151d5a8431f7f5e..7a39beff283dd249bbb29c4e59ef83557722e106 100644 (file)
@@ -293,7 +293,6 @@ pipe_update_debug(struct proto *P)
 struct protocol proto_pipe = {
   .name =              "Pipe",
   .template =          "pipe%d",
-  .class =             PROTOCOL_PIPE,
   .proto_size =                sizeof(struct pipe_proto),
   .config_size =       sizeof(struct pipe_config),
   .postconfig =                pipe_postconfig,
index 0a339cb4ac29ffd45805bdc871d031b8203c880b..f40fdcca0f04210ed47717fd3ab438e6c23a94a0 100644 (file)
@@ -336,9 +336,6 @@ radv_sensitive:
  | SENSITIVE bool { $$ = $2; }
  ;
 
-dynamic_attr: RA_PREFERENCE { $$ = f_new_dynamic_attr(T_ENUM_RA_PREFERENCE, EA_RA_PREFERENCE); } ;
-dynamic_attr: RA_LIFETIME { $$ = f_new_dynamic_attr(T_INT, EA_RA_LIFETIME); } ;
-
 CF_CODE
 
 CF_END
index 7985997a32baca767014b0a51addd3633f1eb53d..5734dbc9b34e040a728d9660c91188f225aced90 100644 (file)
@@ -10,6 +10,7 @@
 
 #include <stdlib.h>
 #include "radv.h"
+#include "lib/macro.h"
 
 /**
  * DOC: Router Advertisements
@@ -42,6 +43,8 @@
  * RFC 6106 - DNS extensions (RDDNS, DNSSL)
  */
 
+static struct ea_class ea_radv_preference, ea_radv_lifetime;
+
 static void radv_prune_prefixes(struct radv_iface *ifa);
 static void radv_prune_routes(struct radv_proto *p);
 
@@ -444,11 +447,11 @@ radv_rt_notify(struct proto *P, struct channel *ch UNUSED, net *n, rte *new, rte
   {
     /* Update */
 
-    ea = ea_find(new->attrs->eattrs, EA_RA_PREFERENCE);
+    ea = ea_find(new->attrs->eattrs, &ea_radv_preference);
     uint preference = ea ? ea->u.data : RA_PREF_MEDIUM;
     uint preference_set = !!ea;
 
-    ea = ea_find(new->attrs->eattrs, EA_RA_LIFETIME);
+    ea = ea_find(new->attrs->eattrs, &ea_radv_lifetime);
     uint lifetime = ea ? ea->u.data : 0;
     uint lifetime_set = !!ea;
 
@@ -738,27 +741,26 @@ radv_pref_str(u32 pref)
   }
 }
 
-/* The buffer has some minimal size */
-static int
-radv_get_attr(const eattr *a, byte *buf, int buflen UNUSED)
+static void
+radv_preference_format(const eattr *a, byte *buf, uint buflen)
 {
-  switch (a->id)
-  {
-  case EA_RA_PREFERENCE:
-    bsprintf(buf, "preference: %s", radv_pref_str(a->u.data));
-    return GA_FULL;
-  case EA_RA_LIFETIME:
-    bsprintf(buf, "lifetime");
-    return GA_NAME;
-  default:
-    return GA_UNKNOWN;
-  }
+  bsnprintf(buf, buflen, "%s", radv_pref_str(a->u.data));
 }
 
+static struct ea_class ea_radv_preference = {
+  .name = "radv_preference",
+  .type = T_ENUM_RA_PREFERENCE,
+  .format = radv_preference_format,
+};
+
+static struct ea_class ea_radv_lifetime = {
+  .name = "radv_lifetime",
+  .type = T_INT,
+};
+
 struct protocol proto_radv = {
   .name =              "RAdv",
   .template =          "radv%d",
-  .class =             PROTOCOL_RADV,
   .channel_mask =      NB_IP6,
   .proto_size =                sizeof(struct radv_proto),
   .config_size =       sizeof(struct radv_config),
@@ -769,11 +771,15 @@ struct protocol proto_radv = {
   .reconfigure =       radv_reconfigure,
   .copy_config =       radv_copy_config,
   .get_status =                radv_get_status,
-  .get_attr =          radv_get_attr
 };
 
 void
 radv_build(void)
 {
   proto_build(&proto_radv);
+
+  EA_REGISTER_ALL(
+      &ea_radv_preference,
+      &ea_radv_lifetime
+      );
 }
index 5cca3acae83bf83c3a15221e49f1425cfd54dbc4..c9219bdaea77209462e1227657a07e440d6cfdb3 100644 (file)
@@ -195,10 +195,6 @@ struct radv_iface
 #define RA_PREF_HIGH   0x08
 #define RA_PREF_MASK   0x18
 
-/* Attributes */
-#define EA_RA_PREFERENCE       EA_CODE(PROTOCOL_RADV, 0)
-#define EA_RA_LIFETIME         EA_CODE(PROTOCOL_RADV, 1)
-
 #ifdef LOCAL_DEBUG
 #define RADV_FORCE_DEBUG 1
 #else
index 3934d337fcf89a44598478ffd39f37c4687249ca..234e90291fab72b192c2ec4065f4a4c0b9ea0cf1 100644 (file)
@@ -190,9 +190,6 @@ rip_iface:
   rip_iface_start iface_patt_list_nopx rip_iface_opt_list rip_iface_finish;
 
 
-dynamic_attr: RIP_METRIC { $$ = f_new_dynamic_attr(T_INT, EA_RIP_METRIC); } ;
-dynamic_attr: RIP_TAG { $$ = f_new_dynamic_attr(T_INT, EA_RIP_TAG); } ;
-
 CF_CLI_HELP(SHOW RIP, ..., [[Show information about RIP protocol]]);
 
 CF_CLI(SHOW RIP INTERFACES, optproto opttext, [<name>] [\"<interface>\"], [[Show information about RIP interfaces]])
index 2b5babcb01023438e817b3d8ea1fa540c14e7eb2..52a3bd2c9227e0479af21569bbd504ce199ea551 100644 (file)
@@ -78,6 +78,7 @@
 
 #include <stdlib.h>
 #include "rip.h"
+#include "lib/macro.h"
 
 
 static inline void rip_lock_neighbor(struct rip_neighbor *n);
@@ -88,6 +89,7 @@ static inline void rip_iface_kick_timer(struct rip_iface *ifa);
 static void rip_iface_timer(timer *timer);
 static void rip_trigger_update(struct rip_proto *p);
 
+static struct ea_class ea_rip_metric, ea_rip_tag, ea_rip_from;
 
 /*
  *     RIP routes
@@ -200,9 +202,9 @@ rip_announce_rte(struct rip_proto *p, struct rip_entry *en)
     } ea_block = {
       .l.count = 3,
       .a = {
-       EA_LITERAL_EMBEDDED(EA_RIP_METRIC, T_INT, 0, rt_metric),
-       EA_LITERAL_EMBEDDED(EA_RIP_TAG, T_INT, 0, rt_tag),
-       EA_LITERAL_DIRECT_ADATA(EA_RIP_FROM, T_IFACE, 0, &ea_block.riad.ad),
+       EA_LITERAL_EMBEDDED(&ea_rip_metric, 0, rt_metric),
+       EA_LITERAL_EMBEDDED(&ea_rip_tag, 0, rt_tag),
+       EA_LITERAL_DIRECT_ADATA(&ea_rip_from, 0, &ea_block.riad.ad),
       },
       .riad = {
        .ad = { .length = sizeof(struct rip_iface_adata) - sizeof(struct adata) },
@@ -326,9 +328,9 @@ rip_rt_notify(struct proto *P, struct channel *ch UNUSED, struct network *net, s
   if (new)
   {
     /* Update */
-    u32 rt_tag = ea_get_int(new->attrs->eattrs, EA_RIP_TAG, 0);
-    u32 rt_metric = ea_get_int(new->attrs->eattrs, EA_RIP_METRIC, 1);
-    const eattr *rie = ea_find(new->attrs->eattrs, EA_RIP_FROM);
+    u32 rt_tag = ea_get_int(new->attrs->eattrs, &ea_rip_tag, 0);
+    u32 rt_metric = ea_get_int(new->attrs->eattrs, &ea_rip_metric, 1);
+    const eattr *rie = ea_find(new->attrs->eattrs, &ea_rip_from);
     struct iface *rt_from = rie ? ((struct rip_iface_adata *) rie->u.ptr)->iface : NULL;
 
     if (rt_metric > p->infinity)
@@ -1095,8 +1097,8 @@ rip_rte_better(struct rte *new, struct rte *old)
   ASSERT_DIE(new->src == old->src);
   struct rip_proto *p = (struct rip_proto *) new->src->proto;
 
-  u32 new_metric = ea_get_int(new->attrs->eattrs, EA_RIP_METRIC, p->infinity);
-  u32 old_metric = ea_get_int(old->attrs->eattrs, EA_RIP_METRIC, p->infinity);
+  u32 new_metric = ea_get_int(new->attrs->eattrs, &ea_rip_metric, p->infinity);
+  u32 old_metric = ea_get_int(old->attrs->eattrs, &ea_rip_metric, p->infinity);
 
   return new_metric < old_metric;
 }
@@ -1104,7 +1106,7 @@ rip_rte_better(struct rte *new, struct rte *old)
 static u32
 rip_rte_igp_metric(struct rte *rt)
 {
-  return ea_get_int(rt->attrs->eattrs, EA_RIP_METRIC, IGP_METRIC_UNKNOWN);
+  return ea_get_int(rt->attrs->eattrs, &ea_rip_metric, IGP_METRIC_UNKNOWN);
 }
 
 static void
@@ -1205,8 +1207,8 @@ static void
 rip_get_route_info(rte *rte, byte *buf)
 {
   struct rip_proto *p = (struct rip_proto *) rte->src->proto;
-  u32 rt_metric = ea_get_int(rte->attrs->eattrs, EA_RIP_METRIC, p->infinity);
-  u32 rt_tag = ea_get_int(rte->attrs->eattrs, EA_RIP_TAG, 0);
+  u32 rt_metric = ea_get_int(rte->attrs->eattrs, &ea_rip_metric, p->infinity);
+  u32 rt_tag = ea_get_int(rte->attrs->eattrs, &ea_rip_tag, 0);
 
   buf += bsprintf(buf, " (%d/%d)", rte->attrs->pref, rt_metric);
 
@@ -1214,23 +1216,28 @@ rip_get_route_info(rte *rte, byte *buf)
     bsprintf(buf, " [%04x]", rt_tag);
 }
 
-static int
-rip_get_attr(const eattr *a, byte *buf, int buflen UNUSED)
+static void
+rip_tag_format(const eattr *a, byte *buf, uint buflen)
 {
-  switch (a->id)
-  {
-  case EA_RIP_METRIC:
-    bsprintf(buf, "metric: %d", a->u.data);
-    return GA_FULL;
+  bsnprintf(buf, buflen, "tag: %04x", a->u.data);
+}
 
-  case EA_RIP_TAG:
-    bsprintf(buf, "tag: %04x", a->u.data);
-    return GA_FULL;
+static struct ea_class ea_rip_metric = {
+  .name = "rip_metric",
+  .type = T_INT,
+};
 
-  default:
-    return GA_UNKNOWN;
-  }
-}
+static struct ea_class ea_rip_tag = {
+  .name = "rip_tag",
+  .type = T_INT,
+  .format = rip_tag_format,
+};
+
+static struct ea_class ea_rip_from = {
+  .name = "rip_from",
+  .type = T_IFACE,
+  .readonly = 1,
+};
 
 void
 rip_show_interfaces(struct proto *P, const char *iff)
@@ -1334,7 +1341,6 @@ rip_dump(struct proto *P)
 struct protocol proto_rip = {
   .name =              "RIP",
   .template =          "rip%d",
-  .class =             PROTOCOL_RIP,
   .preference =                DEF_PREF_RIP,
   .channel_mask =      NB_IP,
   .proto_size =                sizeof(struct rip_proto),
@@ -1346,11 +1352,16 @@ struct protocol proto_rip = {
   .shutdown =          rip_shutdown,
   .reconfigure =       rip_reconfigure,
   .get_route_info =    rip_get_route_info,
-  .get_attr =          rip_get_attr
 };
 
 void
 rip_build(void)
 {
   proto_build(&proto_rip);
+
+  EA_REGISTER_ALL(
+      &ea_rip_metric,
+      &ea_rip_tag,
+      &ea_rip_from
+      );
 }
index a6fa332668e5cfaba7a227fa782c8e8ff2faf870..a01f8d3b4b36d5973ba0d41b050c3434f092dedf 100644 (file)
@@ -195,10 +195,6 @@ struct rip_rte
 #define RIP_ENTRY_VALID                1       /* Valid outgoing route */
 #define RIP_ENTRY_STALE                2       /* Stale outgoing route, waiting for GC */
 
-#define EA_RIP_METRIC          EA_CODE(PROTOCOL_RIP, 0)
-#define EA_RIP_TAG             EA_CODE(PROTOCOL_RIP, 1)
-#define EA_RIP_FROM            EA_CODE(PROTOCOL_RIP, 2)
-
 static inline int rip_is_v2(struct rip_proto *p)
 { return p->rip2; }
 
index 6e111a81fd9afd8fe5f390f67d1f8f08b8857f09..d4e95a83dc4e5e56fced2be097aa6e55fbf23c64 100644 (file)
@@ -935,7 +935,6 @@ rpki_copy_config(struct proto_config *dest UNUSED, struct proto_config *src UNUS
 struct protocol proto_rpki = {
   .name =              "RPKI",
   .template =          "rpki%d",
-  .class =             PROTOCOL_RPKI,
   .preference =        DEF_PREF_RPKI,
   .proto_size =        sizeof(struct rpki_proto),
   .config_size =       sizeof(struct rpki_config),
index 42febcd42e736d78480b879472d4d8d0ed21e5f2..090ec87503b7b5bd789a2fdee259448337f59fd6 100644 (file)
@@ -408,16 +408,16 @@ static_reload_routes(struct channel *C)
 static int
 static_rte_better(rte *new, rte *old)
 {
-  u32 n = ea_get_int(new->attrs->eattrs, EA_GEN_IGP_METRIC, IGP_METRIC_UNKNOWN);
-  u32 o = ea_get_int(old->attrs->eattrs, EA_GEN_IGP_METRIC, IGP_METRIC_UNKNOWN);
+  u32 n = ea_get_int(new->attrs->eattrs, &ea_gen_igp_metric, IGP_METRIC_UNKNOWN);
+  u32 o = ea_get_int(old->attrs->eattrs, &ea_gen_igp_metric, IGP_METRIC_UNKNOWN);
   return n < o;
 }
 
 static int
 static_rte_mergable(rte *pri, rte *sec)
 {
-  u32 a = ea_get_int(pri->attrs->eattrs, EA_GEN_IGP_METRIC, IGP_METRIC_UNKNOWN);
-  u32 b = ea_get_int(sec->attrs->eattrs, EA_GEN_IGP_METRIC, IGP_METRIC_UNKNOWN);
+  u32 a = ea_get_int(pri->attrs->eattrs, &ea_gen_igp_metric, IGP_METRIC_UNKNOWN);
+  u32 b = ea_get_int(sec->attrs->eattrs, &ea_gen_igp_metric, IGP_METRIC_UNKNOWN);
   return a == b;
 }
 
@@ -711,7 +711,7 @@ static_copy_config(struct proto_config *dest, struct proto_config *src)
 static void
 static_get_route_info(rte *rte, byte *buf)
 {
-  eattr *a = ea_find(rte->attrs->eattrs, EA_GEN_IGP_METRIC);
+  eattr *a = ea_find(rte->attrs->eattrs, &ea_gen_igp_metric);
   if (a)
     buf += bsprintf(buf, " (%d/%u)", rte->attrs->pref, a->u.data);
   else
@@ -769,7 +769,6 @@ static_show(struct proto *P)
 struct protocol proto_static = {
   .name =              "Static",
   .template =          "static%d",
-  .class =             PROTOCOL_STATIC,
   .preference =                DEF_PREF_STATIC,
   .channel_mask =      NB_ANY,
   .proto_size =                sizeof(struct static_proto),
index 8897f889fbff8a2b660d68d6e533988c421e3ef9..aa90f6e4af1ad8d495f9c1736b40c7d6229ad417 100644 (file)
@@ -34,38 +34,6 @@ static inline struct ifa * kif_get_primary_ip(struct iface *i UNUSED) { return N
 
 #define KRT_ALLOW_MERGE_PATHS  1
 
-#define EA_KRT_PREFSRC         EA_CODE(PROTOCOL_KERNEL, 0x10)
-#define EA_KRT_REALM           EA_CODE(PROTOCOL_KERNEL, 0x11)
-#define EA_KRT_SCOPE           EA_CODE(PROTOCOL_KERNEL, 0x12)
-
-
-#define KRT_METRICS_MAX                0x10    /* RTAX_QUICKACK+1 */
-#define KRT_METRICS_OFFSET     0x20    /* Offset of EA_KRT_* vs RTAX_* */
-
-#define KRT_FEATURES_MAX       4
-
-/*
- * Following attributes are parts of RTA_METRICS kernel route attribute, their
- * ids must be consistent with their RTAX_* constants (+ KRT_METRICS_OFFSET)
- */
-#define EA_KRT_METRICS         EA_CODE(PROTOCOL_KERNEL, 0x20)  /* Dummy one */
-#define EA_KRT_LOCK            EA_CODE(PROTOCOL_KERNEL, 0x21)
-#define EA_KRT_MTU             EA_CODE(PROTOCOL_KERNEL, 0x22)
-#define EA_KRT_WINDOW          EA_CODE(PROTOCOL_KERNEL, 0x23)
-#define EA_KRT_RTT             EA_CODE(PROTOCOL_KERNEL, 0x24)
-#define EA_KRT_RTTVAR          EA_CODE(PROTOCOL_KERNEL, 0x25)
-#define EA_KRT_SSTRESH         EA_CODE(PROTOCOL_KERNEL, 0x26)
-#define EA_KRT_CWND            EA_CODE(PROTOCOL_KERNEL, 0x27)
-#define EA_KRT_ADVMSS          EA_CODE(PROTOCOL_KERNEL, 0x28)
-#define EA_KRT_REORDERING      EA_CODE(PROTOCOL_KERNEL, 0x29)
-#define EA_KRT_HOPLIMIT                EA_CODE(PROTOCOL_KERNEL, 0x2a)
-#define EA_KRT_INITCWND                EA_CODE(PROTOCOL_KERNEL, 0x2b)
-#define EA_KRT_FEATURES                EA_CODE(PROTOCOL_KERNEL, 0x2c)
-#define EA_KRT_RTO_MIN         EA_CODE(PROTOCOL_KERNEL, 0x2d)
-#define EA_KRT_INITRWND                EA_CODE(PROTOCOL_KERNEL, 0x2e)
-#define EA_KRT_QUICKACK                EA_CODE(PROTOCOL_KERNEL, 0x2f)
-
-
 struct krt_params {
   u32 table_id;                                /* Kernel table ID we sync with */
   u32 metric;                          /* Kernel metric used for all routes */
index f31b88c7a3cf962fd3878b110200f99cfc3a5e7a..17e1778973ea7dac87b2d80e61c3bde4d6cfe10d 100644 (file)
@@ -28,39 +28,22 @@ kern_sys_item:
  | NETLINK RX BUFFER expr { THIS_KRT->sys.netlink_rx_buffer = $4; }
  ;
 
-dynamic_attr: KRT_PREFSRC      { $$ = f_new_dynamic_attr(T_IP, EA_KRT_PREFSRC); } ;
-dynamic_attr: KRT_REALM        { $$ = f_new_dynamic_attr(T_INT, EA_KRT_REALM); } ;
-dynamic_attr: KRT_SCOPE        { $$ = f_new_dynamic_attr(T_INT, EA_KRT_SCOPE); } ;
-
-dynamic_attr: KRT_MTU          { $$ = f_new_dynamic_attr(T_INT, EA_KRT_MTU); } ;
-dynamic_attr: KRT_WINDOW       { $$ = f_new_dynamic_attr(T_INT, EA_KRT_WINDOW); } ;
-dynamic_attr: KRT_RTT          { $$ = f_new_dynamic_attr(T_INT, EA_KRT_RTT); } ;
-dynamic_attr: KRT_RTTVAR       { $$ = f_new_dynamic_attr(T_INT, EA_KRT_RTTVAR); } ;
-dynamic_attr: KRT_SSTRESH      { $$ = f_new_dynamic_attr(T_INT, EA_KRT_SSTRESH); } ;
-dynamic_attr: KRT_CWND         { $$ = f_new_dynamic_attr(T_INT, EA_KRT_CWND); } ;
-dynamic_attr: KRT_ADVMSS       { $$ = f_new_dynamic_attr(T_INT, EA_KRT_ADVMSS); } ;
-dynamic_attr: KRT_REORDERING   { $$ = f_new_dynamic_attr(T_INT, EA_KRT_REORDERING); } ;
-dynamic_attr: KRT_HOPLIMIT     { $$ = f_new_dynamic_attr(T_INT, EA_KRT_HOPLIMIT); } ;
-dynamic_attr: KRT_INITCWND     { $$ = f_new_dynamic_attr(T_INT, EA_KRT_INITCWND); } ;
-dynamic_attr: KRT_RTO_MIN      { $$ = f_new_dynamic_attr(T_INT, EA_KRT_RTO_MIN); } ;
-dynamic_attr: KRT_INITRWND     { $$ = f_new_dynamic_attr(T_INT, EA_KRT_INITRWND); } ;
-dynamic_attr: KRT_QUICKACK     { $$ = f_new_dynamic_attr(T_INT, EA_KRT_QUICKACK); } ;
-
 /* Bits of EA_KRT_LOCK, based on RTAX_* constants */
 
-attr_bit: KRT_LOCK_MTU { $$ = f_new_dynamic_attr_bit(2, EA_KRT_LOCK); } ;
-attr_bit: KRT_LOCK_WINDOW      { $$ = f_new_dynamic_attr_bit(3, EA_KRT_LOCK); } ;
-attr_bit: KRT_LOCK_RTT { $$ = f_new_dynamic_attr_bit(4, EA_KRT_LOCK); } ;
-attr_bit: KRT_LOCK_RTTVAR      { $$ = f_new_dynamic_attr_bit(5, EA_KRT_LOCK); } ;
-attr_bit: KRT_LOCK_SSTRESH     { $$ = f_new_dynamic_attr_bit(6, EA_KRT_LOCK); } ;
-attr_bit: KRT_LOCK_CWND        { $$ = f_new_dynamic_attr_bit(7, EA_KRT_LOCK); } ;
-attr_bit: KRT_LOCK_ADVMSS      { $$ = f_new_dynamic_attr_bit(8, EA_KRT_LOCK); } ;
-attr_bit: KRT_LOCK_REORDERING { $$ = f_new_dynamic_attr_bit(9, EA_KRT_LOCK); } ;
-attr_bit: KRT_LOCK_HOPLIMIT { $$ = f_new_dynamic_attr_bit(10, EA_KRT_LOCK); } ;
-attr_bit: KRT_LOCK_RTO_MIN { $$ = f_new_dynamic_attr_bit(13, EA_KRT_LOCK); } ;
-
-attr_bit: KRT_FEATURE_ECN      { $$ = f_new_dynamic_attr_bit(0, EA_KRT_FEATURES); } ;
-attr_bit: KRT_FEATURE_ALLFRAG { $$ = f_new_dynamic_attr_bit(3, EA_KRT_FEATURES); } ;
+attr_bit: KRT_LOCK_MTU { $$ = f_new_dynamic_attr_bit(2, "krt_lock"); } ;
+attr_bit: KRT_LOCK_WINDOW      { $$ = f_new_dynamic_attr_bit(3, "krt_lock"); } ;
+attr_bit: KRT_LOCK_RTT { $$ = f_new_dynamic_attr_bit(4, "krt_lock"); } ;
+attr_bit: KRT_LOCK_RTTVAR      { $$ = f_new_dynamic_attr_bit(5, "krt_lock"); } ;
+attr_bit: KRT_LOCK_SSTRESH     { $$ = f_new_dynamic_attr_bit(6, "krt_lock"); } ;
+attr_bit: KRT_LOCK_CWND        { $$ = f_new_dynamic_attr_bit(7, "krt_lock"); } ;
+attr_bit: KRT_LOCK_ADVMSS      { $$ = f_new_dynamic_attr_bit(8, "krt_lock"); } ;
+attr_bit: KRT_LOCK_REORDERING { $$ = f_new_dynamic_attr_bit(9, "krt_lock"); } ;
+attr_bit: KRT_LOCK_HOPLIMIT { $$ = f_new_dynamic_attr_bit(10, "krt_lock"); } ;
+attr_bit: KRT_LOCK_RTO_MIN { $$ = f_new_dynamic_attr_bit(13, "krt_lock"); } ;
+
+/* Bits of EA_KRT_FEATURES */
+attr_bit: KRT_FEATURE_ECN      { $$ = f_new_dynamic_attr_bit(0, "krt_features"); } ;
+attr_bit: KRT_FEATURE_ALLFRAG { $$ = f_new_dynamic_attr_bit(3, "krt_features"); } ;
 
 
 CF_CODE
index 81e02b4ca30c6cb6873f653eb04d7d363cf2c91a..23d41b56c17342b54e2c628c77f68e28dd4d6777 100644 (file)
@@ -26,6 +26,7 @@
 #include "lib/socket.h"
 #include "lib/string.h"
 #include "lib/hash.h"
+#include "lib/macro.h"
 #include "conf/conf.h"
 
 #include <asm/types.h>
@@ -121,6 +122,101 @@ struct nl_parse_state
   u32 rta_flow;                /* Used during parsing */
 };
 
+/*
+ *     Netlink eattr definitions
+ */
+
+#define KRT_METRICS_MAX                ARRAY_SIZE(ea_krt_metrics)
+#define KRT_FEATURES_MAX       4
+
+static void krt_bitfield_format(const eattr *e, byte *buf, uint buflen);
+
+static struct ea_class
+  ea_krt_prefsrc = {
+    .name = "krt_prefsrc",
+    .type = T_IP,
+  },
+  ea_krt_realm = {
+    .name = "krt_realm",
+    .type = T_INT,
+  },
+  ea_krt_scope = {
+    .name = "krt_scope",
+    .type = T_INT,
+  };
+
+static struct ea_class ea_krt_metrics[] = {
+  [RTAX_LOCK] = {
+    .name = "krt_lock",
+    .type = T_INT,
+    .format = krt_bitfield_format,
+  },
+  [RTAX_FEATURES] = {
+    .name = "krt_features",
+    .type = T_INT,
+    .format = krt_bitfield_format,
+  },
+#define KRT_METRIC_INT(_rtax, _name)   [_rtax] = { .name = _name, .type = T_INT }
+  KRT_METRIC_INT(RTAX_MTU, "krt_mtu"),
+  KRT_METRIC_INT(RTAX_WINDOW, "krt_window"),
+  KRT_METRIC_INT(RTAX_RTT, "krt_rtt"),
+  KRT_METRIC_INT(RTAX_RTTVAR, "krt_rttvar"),
+  KRT_METRIC_INT(RTAX_SSTHRESH, "krt_sstresh"),
+  KRT_METRIC_INT(RTAX_CWND, "krt_cwnd"),
+  KRT_METRIC_INT(RTAX_ADVMSS, "krt_advmss"),
+  KRT_METRIC_INT(RTAX_REORDERING, "krt_reordering"),
+  KRT_METRIC_INT(RTAX_HOPLIMIT, "krt_hoplimit"),
+  KRT_METRIC_INT(RTAX_INITCWND, "krt_initcwnd"),
+  KRT_METRIC_INT(RTAX_RTO_MIN, "krt_rto_min"),
+  KRT_METRIC_INT(RTAX_INITRWND, "krt_initrwnd"),
+  KRT_METRIC_INT(RTAX_QUICKACK, "krt_quickack"),
+#undef KRT_METRIC_INT
+};
+
+static const char *krt_metrics_names[KRT_METRICS_MAX] = {
+  NULL, "lock", "mtu", "window", "rtt", "rttvar", "sstresh", "cwnd", "advmss",
+  "reordering", "hoplimit", "initcwnd", "features", "rto_min", "initrwnd", "quickack"
+};
+
+static const char *krt_features_names[KRT_FEATURES_MAX] = {
+  "ecn", NULL, NULL, "allfrag"
+};
+
+static void
+krt_bitfield_format(const eattr *a, byte *buf, uint buflen)
+{
+  if (a->id == ea_krt_metrics[RTAX_LOCK].id)
+    ea_format_bitfield(a, buf, buflen, krt_metrics_names, 2, KRT_METRICS_MAX);
+  else if (a->id == ea_krt_metrics[RTAX_FEATURES].id)
+    ea_format_bitfield(a, buf, buflen, krt_features_names, 0, KRT_FEATURES_MAX);
+}
+
+static void
+nl_ea_register(void)
+{
+  EA_REGISTER_ALL(
+      &ea_krt_prefsrc,
+      &ea_krt_realm,
+      &ea_krt_scope
+      );
+
+  for (uint i = 0; i < KRT_METRICS_MAX; i++)
+  {
+    if (!ea_krt_metrics[i].name)
+      ea_krt_metrics[i] = (struct ea_class) {
+       .name = mb_sprintf(&root_pool, "krt_metric_%d", i),
+       .type = T_INT,
+      };
+
+    ea_register_init(&ea_krt_metrics[i]);
+  }
+
+  for (uint i = 1; i < KRT_METRICS_MAX; i++)
+    ASSERT_DIE(ea_krt_metrics[i].id == ea_krt_metrics[0].id + i);
+}
+
+
+
 /*
  *     Synchronous Netlink interface
  */
@@ -734,7 +830,7 @@ static void
 nl_add_multipath(struct nlmsghdr *h, uint bufsize, struct nexthop *nh, int af, ea_list *eattrs)
 {
   struct rtattr *a = nl_open_attr(h, bufsize, RTA_MULTIPATH);
-  eattr *flow = ea_find(eattrs, EA_KRT_REALM);
+  eattr *flow = ea_find(eattrs, &ea_krt_realm);
 
   for (; nh; nh = nh->next)
   {
@@ -1399,7 +1495,7 @@ nl_send_route(struct krt_proto *p, rte *e, int op, int dest, struct nexthop *nh)
     priority = 0;
   else if (KRT_CF->sys.metric)
     priority = KRT_CF->sys.metric;
-  else if ((op != NL_OP_DELETE) && (ea = ea_find(eattrs, EA_KRT_METRIC)))
+  else if ((op != NL_OP_DELETE) && (ea = ea_find(eattrs, &ea_krt_metric)))
     priority = ea->u.data;
 
   if (priority)
@@ -1412,15 +1508,15 @@ nl_send_route(struct krt_proto *p, rte *e, int op, int dest, struct nexthop *nh)
   /* Default scope is LINK for device routes, UNIVERSE otherwise */
   if (p->af == AF_MPLS)
     r->r.rtm_scope = RT_SCOPE_UNIVERSE;
-  else if (ea = ea_find(eattrs, EA_KRT_SCOPE))
+  else if (ea = ea_find(eattrs, &ea_krt_scope))
     r->r.rtm_scope = ea->u.data;
   else
     r->r.rtm_scope = (dest == RTD_UNICAST && ipa_zero(nh->gw)) ? RT_SCOPE_LINK : RT_SCOPE_UNIVERSE;
 
-  if (ea = ea_find(eattrs, EA_KRT_PREFSRC))
+  if (ea = ea_find(eattrs, &ea_krt_prefsrc))
     nl_add_attr_ipa(&r->h, rsize, RTA_PREFSRC, *(ip_addr *)ea->u.ptr->data);
 
-  if (ea = ea_find(eattrs, EA_KRT_REALM))
+  if (ea = ea_find(eattrs, &ea_krt_realm))
     nl_add_attr_u32(&r->h, rsize, RTA_FLOW, ea->u.data);
 
 
@@ -1428,9 +1524,9 @@ nl_send_route(struct krt_proto *p, rte *e, int op, int dest, struct nexthop *nh)
   metrics[0] = 0;
 
   struct ea_walk_state ews = { .eattrs = eattrs };
-  while (ea = ea_walk(&ews, EA_KRT_METRICS, KRT_METRICS_MAX))
+  while (ea = ea_walk(&ews, ea_krt_metrics[0].id, KRT_METRICS_MAX))
   {
-    int id = ea->id - EA_KRT_METRICS;
+    int id = ea->id - ea_krt_metrics[0].id;
     metrics[0] |= 1 << id;
     metrics[id] = ea->u.data;
   }
@@ -1581,21 +1677,15 @@ nl_announce_route(struct nl_parse_state *s)
   rte *e = rte_get_temp(s->attrs, s->proto->p.main_source);
   e->net = s->net;
 
-  EA_LOCAL_LIST(2) ea0 = {
+  EA_LOCAL_LIST(2) ea = {
     .l = { .count = 2, .next = e->attrs->eattrs },
-    .a[0] = (eattr) {
-      .id = EA_KRT_SOURCE,
-      .type = T_INT,
-      .u.data = s->krt_proto,
-    },
-    .a[1] = (eattr) {
-      .id = EA_KRT_METRIC,
-      .type = T_INT,
-      .u.data = s->krt_metric,
+    .a = {
+      EA_LITERAL_EMBEDDED(&ea_krt_source, 0, s->krt_proto),
+      EA_LITERAL_EMBEDDED(&ea_krt_metric, 0, s->krt_metric),
     },
   };
 
-  e->attrs->eattrs = &ea0.l;
+  e->attrs->eattrs = &ea.l;
 
   if (s->scan)
     krt_got_route(s->proto, e, s->krt_src);
@@ -1867,20 +1957,20 @@ nl_parse_route(struct nl_parse_state *s, struct nlmsghdr *h)
 
   if (i->rtm_scope != def_scope)
     ea_set_attr(&ra->eattrs,
-       EA_LITERAL_EMBEDDED(EA_KRT_SCOPE, T_INT, 0, i->rtm_scope));
+       EA_LITERAL_EMBEDDED(&ea_krt_scope, 0, i->rtm_scope));
 
   if (a[RTA_PREFSRC])
-    {
-      ip_addr ps = rta_get_ipa(a[RTA_PREFSRC]);
+  {
+    ip_addr ps = rta_get_ipa(a[RTA_PREFSRC]);
 
-      ea_set_attr(&ra->eattrs,
-         EA_LITERAL_STORE_ADATA(EA_KRT_PREFSRC, T_IP, 0, &ps, sizeof(ps)));
-    }
+    ea_set_attr(&ra->eattrs,
+       EA_LITERAL_STORE_ADATA(&ea_krt_prefsrc, 0, &ps, sizeof(ps)));
+  }
 
   /* Can be set per-route or per-nexthop */
   if (s->rta_flow)
     ea_set_attr(&ra->eattrs,
-       EA_LITERAL_EMBEDDED(EA_KRT_REALM, T_INT, 0, s->rta_flow));
+       EA_LITERAL_EMBEDDED(&ea_krt_realm, 0, s->rta_flow));
 
   if (a[RTA_METRICS])
     {
@@ -1891,11 +1981,10 @@ nl_parse_route(struct nl_parse_state *s, struct nlmsghdr *h)
          return;
        }
 
-      for (int t = 1; t < KRT_METRICS_MAX; t++)
+      for (uint t = 1; t < KRT_METRICS_MAX; t++)
        if (metrics[0] & (1 << t))
          ea_set_attr(&ra->eattrs,
-             EA_LITERAL_EMBEDDED(EA_CODE(PROTOCOL_KERNEL, KRT_METRICS_OFFSET + t),
-               T_INT, 0, metrics[t]));
+             EA_LITERAL_EMBEDDED(&ea_krt_metrics[t], 0, metrics[t]));
     }
 
   /*
@@ -2133,6 +2222,8 @@ krt_sys_io_init(void)
 {
   nl_linpool = lp_new_default(krt_pool);
   HASH_INIT(nl_table_map, krt_pool, 6);
+
+  nl_ea_register();
 }
 
 int
@@ -2186,56 +2277,6 @@ krt_sys_copy_config(struct krt_config *d, struct krt_config *s)
   d->sys.metric = s->sys.metric;
 }
 
-static const char *krt_metrics_names[KRT_METRICS_MAX] = {
-  NULL, "lock", "mtu", "window", "rtt", "rttvar", "sstresh", "cwnd", "advmss",
-  "reordering", "hoplimit", "initcwnd", "features", "rto_min", "initrwnd", "quickack"
-};
-
-static const char *krt_features_names[KRT_FEATURES_MAX] = {
-  "ecn", NULL, NULL, "allfrag"
-};
-
-int
-krt_sys_get_attr(const eattr *a, byte *buf, int buflen UNUSED)
-{
-  switch (a->id)
-  {
-  case EA_KRT_PREFSRC:
-    bsprintf(buf, "prefsrc");
-    return GA_NAME;
-
-  case EA_KRT_REALM:
-    bsprintf(buf, "realm");
-    return GA_NAME;
-
-  case EA_KRT_SCOPE:
-    bsprintf(buf, "scope");
-    return GA_NAME;
-
-  case EA_KRT_LOCK:
-    buf += bsprintf(buf, "lock:");
-    ea_format_bitfield(a, buf, buflen, krt_metrics_names, 2, KRT_METRICS_MAX);
-    return GA_FULL;
-
-  case EA_KRT_FEATURES:
-    buf += bsprintf(buf, "features:");
-    ea_format_bitfield(a, buf, buflen, krt_features_names, 0, KRT_FEATURES_MAX);
-    return GA_FULL;
-
-  default:;
-    int id = (int)EA_ID(a->id) - KRT_METRICS_OFFSET;
-    if (id > 0 && id < KRT_METRICS_MAX)
-    {
-      bsprintf(buf, "%s", krt_metrics_names[id]);
-      return GA_NAME;
-    }
-
-    return GA_UNKNOWN;
-  }
-}
-
-
-
 void
 kif_sys_start(struct kif_proto *p UNUSED)
 {
index 2a3f41defd289aada5d0f1f7acf66b0843684ba8..9300e9c8e5d3843352be511aa72413ef6d6da8b9 100644 (file)
@@ -122,9 +122,6 @@ kif_iface:
   kif_iface_start iface_patt_list_nopx kif_iface_opt_list;
 
 
-dynamic_attr: KRT_SOURCE { $$ = f_new_dynamic_attr(T_INT, EA_KRT_SOURCE); } ;
-dynamic_attr: KRT_METRIC { $$ = f_new_dynamic_attr(T_INT, EA_KRT_METRIC); } ;
-
 CF_CODE
 
 CF_END
index 0ebc4fb33b14843e0b3960165da7c003d1bfe7d3..4da51ce2dd9191afe2403fec8a4960f6b833c5c3 100644 (file)
@@ -232,7 +232,6 @@ kif_copy_config(struct proto_config *dest, struct proto_config *src)
 struct protocol proto_unix_iface = {
   .name =              "Device",
   .template =          "device%d",
-  .class =             PROTOCOL_DEVICE,
   .proto_size =                sizeof(struct kif_proto),
   .config_size =       sizeof(struct kif_config),
   .preconfig =         kif_preconfig,
@@ -287,7 +286,7 @@ static struct tbf rl_alien = TBF_DEFAULT_LOG_LIMITS;
 static inline u32
 krt_metric(rte *a)
 {
-  eattr *ea = ea_find(a->attrs->eattrs, EA_KRT_METRIC);
+  eattr *ea = ea_find(a->attrs->eattrs, &ea_krt_metric);
   return ea ? ea->u.data : 0;
 }
 
@@ -1133,24 +1132,15 @@ krt_copy_config(struct proto_config *dest, struct proto_config *src)
   krt_sys_copy_config(d, s);
 }
 
-static int
-krt_get_attr(const eattr *a, byte *buf, int buflen)
-{
-  switch (a->id)
-  {
-  case EA_KRT_SOURCE:
-    bsprintf(buf, "source");
-    return GA_NAME;
-
-  case EA_KRT_METRIC:
-    bsprintf(buf, "metric");
-    return GA_NAME;
-
-  default:
-    return krt_sys_get_attr(a, buf, buflen);
-  }
-}
+struct ea_class ea_krt_source = {
+  .name = "krt_source",
+  .type = T_INT,
+};
 
+struct ea_class ea_krt_metric = {
+  .name = "krt_metric",
+  .type = T_INT,
+};
 
 #ifdef CONFIG_IP6_SADR_KERNEL
 #define MAYBE_IP6_SADR NB_IP6_SADR
@@ -1167,7 +1157,6 @@ krt_get_attr(const eattr *a, byte *buf, int buflen)
 struct protocol proto_unix_kernel = {
   .name =              "Kernel",
   .template =          "kernel%d",
-  .class =             PROTOCOL_KERNEL,
   .preference =                DEF_PREF_INHERITED,
   .channel_mask =      NB_IP | MAYBE_IP6_SADR | MAYBE_MPLS,
   .proto_size =                sizeof(struct krt_proto),
@@ -1179,7 +1168,6 @@ struct protocol proto_unix_kernel = {
   .shutdown =          krt_shutdown,
   .reconfigure =       krt_reconfigure,
   .copy_config =       krt_copy_config,
-  .get_attr =          krt_get_attr,
 #ifdef KRT_ALLOW_LEARN
   .dump =              krt_dump,
 #endif
@@ -1189,4 +1177,9 @@ void
 krt_build(void)
 {
   proto_build(&proto_unix_kernel);
+
+  EA_REGISTER_ALL(
+      &ea_krt_source,
+      &ea_krt_metric,
+      );
 }
index 20858cd79fedd731c2661489528fcbedeb6ce393..04b0416229ac7e0e0a8de2525ea40f6d02d9cc2a 100644 (file)
@@ -21,8 +21,12 @@ struct kif_proto;
 
 #define KRT_DEFAULT_ECMP_LIMIT 16
 
+#if 0
 #define EA_KRT_SOURCE  EA_CODE(PROTOCOL_KERNEL, 0)
 #define EA_KRT_METRIC  EA_CODE(PROTOCOL_KERNEL, 1)
+#endif
+
+extern struct ea_class ea_krt_source, ea_krt_metric;
 
 #define KRT_REF_SEEN   0x1     /* Seen in table */
 #define KRT_REF_BEST   0x2     /* Best in table */
index 9bb37e5da28fa8d2012ade304f4ed90940ec2bd7..8fdad4e6f249363840d776590de08e5fe2d2cecf 100644 (file)
@@ -881,8 +881,8 @@ main(int argc, char **argv)
   resource_init();
   timer_init();
   olock_init();
-  io_init();
   rt_init();
+  io_init();
   if_init();
 //  roa_init();
   config_init();
index ce9a49d767ff2aa9bf99b2f86259be2bcd112094..509b5ed42f1309a8500d50d79530256fcb1e4e53 100644 (file)
@@ -62,8 +62,8 @@ bt_bird_init(void)
 
   olock_init();
   timer_init();
-  io_init();
   rt_init();
+  io_init();
   if_init();
   config_init();
 
@@ -72,9 +72,6 @@ bt_bird_init(void)
 
 void bt_bird_cleanup(void)
 {
-  for (int i = 0; i < PROTOCOL__MAX; i++)
-    class_to_protocol[i] = NULL;
-
   config = new_config = NULL;
 }