]> git.ipfire.org Git - thirdparty/bird.git/commitdiff
Merge branch 'mq-custom' into int-new
authorJan Maria Matejka <mq@ucw.cz>
Thu, 6 Dec 2018 08:55:34 +0000 (09:55 +0100)
committerJan Maria Matejka <mq@ucw.cz>
Thu, 6 Dec 2018 08:55:34 +0000 (09:55 +0100)
conf/conf.h
doc/bird.sgml
filter/config.Y
filter/f-util.c
filter/filter.c
filter/filter.h
nest/config.Y
nest/route.h
nest/rt-attr.c

index 5689fb67272488d9c27c441111f8d930d2288c09..427569fd94bb485b923a435df99659554d388c7b 100644 (file)
@@ -15,7 +15,6 @@
 #include "lib/resource.h"
 #include "lib/timer.h"
 
-
 /* Configuration structure */
 
 struct config {
@@ -128,9 +127,12 @@ struct sym_scope {
 #define SYM_FUNCTION 3
 #define SYM_FILTER 4
 #define SYM_TABLE 5
+#define SYM_ATTRIBUTE 6
 
 #define SYM_VARIABLE 0x100     /* 0x100-0x1ff are variable types */
+#define SYM_VARIABLE_RANGE SYM_VARIABLE ... (SYM_VARIABLE | 0xff)
 #define SYM_CONSTANT 0x200     /* 0x200-0x2ff are variable types */
+#define SYM_CONSTANT_RANGE SYM_CONSTANT ... (SYM_CONSTANT | 0xff)
 
 #define SYM_TYPE(s) (((struct f_val *) (s)->def)->type)
 #define SYM_VAL(s) (((struct f_val *) (s)->def)->val)
index 386d3e8d4c5df9c58014dccffb0b3449ff44cb87..60c38fa43ac090c3d31631386160935327b82a6a 100644 (file)
@@ -25,7 +25,7 @@ configuration - something in config which is not keyword.
 Ondrej Filip <it/&lt;feela@network.cz&gt;/,
 Pavel Machek <it/&lt;pavel@ucw.cz&gt;/,
 Martin Mares <it/&lt;mj@ucw.cz&gt;/,
-Jan Matejka <it/&lt;mq@jmq.cz&gt;/,
+Maria Jan Matejka <it/&lt;mq@jmq.cz&gt;/,
 Ondrej Zajicek <it/&lt;santiago@crfreenet.org&gt;/
 </author>
 
@@ -552,6 +552,12 @@ include "tablename.conf";;
        constants based on /etc/iproute2/rt_* files. A list of defined constants
        can be seen (together with other symbols) using 'show symbols' command.
 
+       <tag><label id="opt-attribute">attribute <m/type/ <m/name/</tag>
+       Define a custom route attribute. You can set and get it in filters like
+       any other route atribute. This feature is intended for marking routes
+       in import filters for export filtering purposes instead of locally
+       assigned BGP communities which have to be deleted in export filters.
+
        <tag><label id="opt-router-id">router id <m/IPv4 address/</tag>
        Set BIRD's router ID. It's a world-wide unique identification of your
        router, usually one of router's IPv4 addresses. Default: the lowest
index d865d11fbb77b864eb9fcecc2e118fba2dc41876..c1e7453102a6aa9a46d8ea5e5cd5abb25d395b16 100644 (file)
@@ -417,7 +417,7 @@ CF_KEYWORDS(FUNCTION, PRINT, PRINTN, UNSET, RETURN,
        ADD, DELETE, CONTAINS, RESET,
        PREPEND, FIRST, LAST, LAST_NONAGGREGATED, MATCH,
        EMPTY,
-       FILTER, WHERE, EVAL,
+       FILTER, WHERE, EVAL, ATTRIBUTE,
        BT_ASSERT, BT_TEST_SUITE, FORMAT)
 
 %nonassoc THEN
@@ -455,6 +455,11 @@ filter_eval:
    EVAL term { f_eval_int($2); }
  ;
 
+conf: custom_attr ;
+custom_attr: ATTRIBUTE type SYM ';' {
+  cf_define_symbol($3, SYM_ATTRIBUTE, ca_lookup(new_config->pool, $3->name, $2)->fda);
+};
+
 conf: bt_test_suite ;
 bt_test_suite:
  BT_TEST_SUITE '(' SYM ',' text ')' {
@@ -834,14 +839,22 @@ function_call:
 
 symbol:
    SYM {
-     switch ($1->class & 0xff00) {
-       case SYM_CONSTANT: $$ = f_new_inst(FI_CONSTANT_INDIRECT); break;
-       case SYM_VARIABLE: $$ = f_new_inst(FI_VARIABLE); break;
-       default: cf_error("%s: variable expected.", $1->name);
+     switch ($1->class & 0xffff) {
+       case SYM_CONSTANT_RANGE:
+         $$ = f_new_inst(FI_CONSTANT_INDIRECT);
+        goto cv_common;
+       case SYM_VARIABLE_RANGE:
+         $$ = f_new_inst(FI_VARIABLE);
+       cv_common:
+         $$->a1.p = $1->def;
+         $$->a2.p = $1->name;
+        break;
+       case SYM_ATTRIBUTE:
+         $$ = f_new_inst_da(FI_EA_GET, *((struct f_dynamic_attr *) $1->def));
+        break;
+       default:
+         cf_error("%s: variable expected.", $1->name);
      }
-
-     $$->a1.p = $1->def;
-     $$->a2.p = $1->name;
    }
 
 static_attr:
@@ -1001,11 +1014,15 @@ cmd:
    }
  | SYM '=' term ';' {
      DBG( "Ook, we'll set value\n" );
-     if (($1->class & ~T_MASK) != SYM_VARIABLE)
-       cf_error( "You may set only variables." );
-     $$ = f_new_inst(FI_SET);
-     $$->a1.p = $1;
-     $$->a2.p = $3;
+     if ($1->class == SYM_ATTRIBUTE) {
+       $$ = f_new_inst_da(FI_EA_SET, *((struct f_dynamic_attr *) $1->def));
+       $$->a1.p = $3;
+     } else if (($1->class & ~T_MASK) == SYM_VARIABLE) {
+       $$ = f_new_inst(FI_SET);
+       $$->a1.p = $1;
+       $$->a2.p = $3;
+     } else
+       cf_error( "Symbol `%s' is read-only.", $1->name );
    }
  | RETURN term ';' {
      DBG( "Ook, we'll return the value\n" );
index 6170760b0769559c68f182381d57fa562ead1d5a..ee9490b46c30381ca7379965406c84647638a94a 100644 (file)
@@ -10,6 +10,9 @@
 #include "nest/bird.h"
 #include "conf/conf.h"
 #include "filter/filter.h"
+#include "lib/idm.h"
+#include "nest/protocol.h"
+#include "nest/route.h"
 
 #define P(a,b) ((a<<8) | b)
 
@@ -105,3 +108,144 @@ filter_name(struct filter *filter)
   else
     return filter->name;
 }
+
+#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 f_type 0x%02x\n",
+      ca->name, ca->fda->ea_code, ca->fda->type, ca->fda->f_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, int f_type)
+{
+  int ea_type;
+
+  switch (f_type) {
+    case T_INT:
+      ea_type = EAF_TYPE_INT;
+      break;
+    case T_IP:
+      ea_type = EAF_TYPE_IP_ADDRESS;
+      break;
+    case T_QUAD:
+      ea_type = EAF_TYPE_ROUTER_ID;
+      break;
+    case T_PATH:
+      ea_type = EAF_TYPE_AS_PATH;
+      break;
+    case T_CLIST:
+      ea_type = EAF_TYPE_INT_SET;
+      break;
+    case T_ECLIST:
+      ea_type = EAF_TYPE_EC_SET;
+      break;
+    case T_LCLIST:
+      ea_type = EAF_TYPE_LC_SET;
+      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, ea_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(ea_type, f_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 f308e7fdc6c47eed6ceb7fce2c5871ee74e038fd..37cf16a3857edd63eb8e8410e4881ef973de9762 100644 (file)
@@ -1063,7 +1063,7 @@ interpret(struct f_inst *what)
        break;
       }
 
-      switch (what->aux & EAF_TYPE_MASK) {
+      switch (e->type & EAF_TYPE_MASK) {
       case EAF_TYPE_INT:
        res.type = f_type;
        res.val.i = e->u.data;
index febfdc651cdd408dc78914ab497f2f1352892258..a8c33287875ac6557c7eb4ae05f1c89861dfcfde 100644 (file)
@@ -287,6 +287,15 @@ struct f_trie
 
 #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, int ea_type);
+
 /* Bird Tests */
 struct f_bt_test_suite {
   node n;                      /* Node in config->tests */
index 88f74b965a7ba6eb1a1e0d69869deb45445e5448..34bde3fa7654a55c90603b55cc949d39bd84bc35 100644 (file)
@@ -532,6 +532,7 @@ r_args:
      $$ = cfg_allocz(sizeof(struct rt_show_data));
      init_list(&($$->tables));
      $$->filter = FILTER_ACCEPT;
+     $$->running_on_config = new_config->fallback;
    }
  | r_args net_any {
      $$ = $1;
@@ -586,7 +587,6 @@ r_args:
      if ($3->class != SYM_PROTO || !c->proto) cf_error("%s is not a protocol", $3->name);
      $$->export_mode = $2;
      $$->export_protocol = c->proto;
-     $$->running_on_config = c->proto->cf->global;
      $$->tables_defined_by = RSD_TDB_INDIRECT;
    }
  | r_args export_mode SYM '.' r_args_channel {
@@ -597,7 +597,6 @@ r_args:
      $$->export_mode = $2;
      $$->export_channel = proto_find_channel_by_name(c->proto, $5);
      if (!$$->export_channel) cf_error("Export channel not found");
-     $$->running_on_config = c->proto->cf->global;
      $$->tables_defined_by = RSD_TDB_INDIRECT;
    }
  | r_args PROTOCOL SYM {
@@ -606,7 +605,6 @@ r_args:
      if ($$->show_protocol) cf_error("Protocol specified twice");
      if ($3->class != SYM_PROTO || !c->proto) cf_error("%s is not a protocol", $3->name);
      $$->show_protocol = c->proto;
-     $$->running_on_config = c->proto->cf->global;
      $$->tables_defined_by = RSD_TDB_INDIRECT;
    }
  | r_args STATS {
index 080bbf583cffe324e81445ce9176992e61df20a4..2600f0871707e4cf02dee5593f185d8e9fd9bed9 100644 (file)
@@ -471,13 +471,20 @@ typedef struct eattr {
   } u;
 } eattr;
 
+
 #define EA_CODE(proto,id) (((proto) << 8) | (id))
-#define EA_PROTO(ea) ((ea) >> 8)
 #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 */
 
index 7a91a4f6267c830ae51d107953500707b24b3b7a..eeeaaa4ce1d24a85683a41b2d9fe6a9e60510353 100644 (file)
@@ -884,7 +884,18 @@ ea_show(struct cli *c, eattr *e)
   byte buf[CLI_MSG_SIZE];
   byte *pos = buf, *end = buf + sizeof(buf);
 
-  if (p = class_to_protocol[EA_PROTO(e->id)])
+  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)