]> git.ipfire.org Git - thirdparty/bird.git/commitdiff
Merge branch 'rt-accepted'
authorOndrej Zajicek <santiago@crfreenet.org>
Mon, 16 Jul 2012 12:44:45 +0000 (14:44 +0200)
committerOndrej Zajicek <santiago@crfreenet.org>
Mon, 16 Jul 2012 12:44:45 +0000 (14:44 +0200)
Conflicts:

nest/config.Y
nest/rt-table.c
proto/bgp/bgp.c

1  2 
nest/config.Y
nest/rt-table.c
proto/bgp/bgp.c
proto/bgp/bgp.h
proto/bgp/config.Y

diff --cc nest/config.Y
index 14cff10a4c90c9621ee8d65b11f4a2e006be8135,2bc5a4ab7b77f18021de83771e7acfd1b87c452b..a75dd0c34846a806913b713eab6b8463cd8451f5
@@@ -44,10 -44,9 +44,10 @@@ CF_DECL
  
  CF_KEYWORDS(ROUTER, ID, PROTOCOL, TEMPLATE, PREFERENCE, DISABLED, DEBUG, ALL, OFF, DIRECT)
  CF_KEYWORDS(INTERFACE, IMPORT, EXPORT, FILTER, NONE, TABLE, STATES, ROUTES, FILTERS)
 +CF_KEYWORDS(LIMIT, ACTION, WARN, BLOCK, RESTART, DISABLE)
  CF_KEYWORDS(PASSWORD, FROM, PASSIVE, TO, ID, EVENTS, PACKETS, PROTOCOLS, INTERFACES)
  CF_KEYWORDS(PRIMARY, STATS, COUNT, FOR, COMMANDS, PREEXPORT, GENERATE, ROA, MAX, FLUSH)
- CF_KEYWORDS(LISTEN, BGP, V6ONLY, DUAL, ADDRESS, PORT, PASSWORDS, DESCRIPTION)
+ CF_KEYWORDS(LISTEN, BGP, V6ONLY, DUAL, ADDRESS, PORT, PASSWORDS, DESCRIPTION, SORTED)
  CF_KEYWORDS(RELOAD, IN, OUT, MRTDUMP, MESSAGES, RESTRICT, MEMORY, IGP_METRIC)
  
  CF_ENUM(T_ENUM_RTS, RTS_, DUMMY, STATIC, INHERIT, DEVICE, STATIC_DEVICE, REDIRECT,
@@@ -65,9 -64,8 +65,9 @@@ CF_ENUM(T_ENUM_ROA, ROA_, UNKNOWN, VALI
  %type <ro> roa_args
  %type <rot> roa_table_arg
  %type <sd> sym_args
- %type <i> proto_start echo_mask echo_size debug_mask debug_list debug_flag mrtdump_mask mrtdump_list mrtdump_flag export_or_preexport roa_mode limit_action
 -%type <i> proto_start echo_mask echo_size debug_mask debug_list debug_flag mrtdump_mask mrtdump_list mrtdump_flag export_or_preexport roa_mode tab_sorted
++%type <i> proto_start echo_mask echo_size debug_mask debug_list debug_flag mrtdump_mask mrtdump_list mrtdump_flag export_or_preexport roa_mode limit_action tab_sorted
  %type <ps> proto_patt proto_patt2
 +%type <g> limit_spec
  
  CF_GRAMMAR
  
diff --cc nest/rt-table.c
index fdc767e7d89127ceb2e216d5d0b065122d59e4f0,eb8304f7909839478e7d4b8bd3235b523c26b810..165f42bb81ecbceeed31d09060ba4ae5ff18d67c
@@@ -198,123 -188,68 +188,115 @@@ export_filter(struct announce_hook *ah
    struct proto *p = ah->proto;
    struct filter *filter = ah->out_filter;
    struct proto_stats *stats = ah->stats;
+   ea_list *tmpb = NULL;
+   rte *rt;
+   int v;
  
-   rte *new0 = new;
-   rte *old0 = old;
-   int ok;
+   rt = rt0;
+   *rt_free = NULL;
  
-   if (new)
+   /* If called does not care for eattrs, we prepare one internally */
+   if (!tmpa)
      {
-       stats->exp_updates_received++;
-       char *drop_reason = NULL;
-       if ((ok = p->import_control ? p->import_control(p, &new, &tmpa, rte_update_pool) : 0) < 0)
-       {
-         stats->exp_updates_rejected++;
-         drop_reason = "rejected by protocol";
-       }
-       else if (ok)
-       rte_trace_out(D_FILTERS, p, new, "forced accept by protocol");
-       else if ((filter == FILTER_REJECT) ||
-              (filter && f_run(filter, &new, &tmpa, rte_update_pool, FF_FORCE_TMPATTR) > F_ACCEPT))
-       {
-         stats->exp_updates_filtered++;
-         drop_reason = "filtered out";
-       }
-       if (drop_reason)
-       {
-         rte_trace_out(D_FILTERS, p, new, drop_reason);
-         if (new != new0)
-           rte_free(new);
-         new = NULL;
-       }
+       struct proto *src = rt->attrs->proto;
+       tmpb = src->make_tmp_attrs ? src->make_tmp_attrs(rt, rte_update_pool) : NULL;
+       tmpa = &tmpb;
      }
-   else
-     stats->exp_withdraws_received++;
  
-   /*
-    * This is a tricky part - we don't know whether route 'old' was
-    * exported to protocol 'p' or was filtered by the export filter.
-    * We try tu run the export filter to know this to have a correct
-    * value in 'old' argument of rte_update (and proper filter value)
-    *
-    * FIXME - this is broken because 'configure soft' may change
-    * filters but keep routes. Refeed is expected to be called after
-    * change of the filters and with old == new, therefore we do not
-    * even try to run the filter on an old route, This may lead to 
-    * 'spurious withdraws' but ensure that there are no 'missing
-    * withdraws'.
-    *
-    * This is not completely safe as there is a window between
-    * reconfiguration and the end of refeed - if a newly filtered
-    * route disappears during this period, proper withdraw is not
-    * sent (because old would be also filtered) and the route is
-    * not refeeded (because it disappeared before that).
-    */
+   v = p->import_control ? p->import_control(p, &rt, tmpa, rte_update_pool) : 0;
+   if (v < 0)
+     {
+       if (silent)
+       goto reject;
  
-   if (old && !refeed)
+       stats->exp_updates_rejected++;
+       rte_trace_out(D_FILTERS, p, rt, "rejected by protocol");
+       goto reject;
+     }
+   if (v > 0)
      {
-       if (filter == FILTER_REJECT)
-       old = NULL;
-       else
-       {
-         ea_list *tmpb = p->make_tmp_attrs ? p->make_tmp_attrs(old, rte_update_pool) : NULL;
-         ok = p->import_control ? p->import_control(p, &old, &tmpb, rte_update_pool) : 0;
-         if (ok < 0 || (!ok && filter && f_run(filter, &old, &tmpb, rte_update_pool, FF_FORCE_TMPATTR) > F_ACCEPT))
-           {
-             if (old != old0)
-               rte_free(old);
-             old = NULL;
-           }
-       }
+       if (!silent)
+       rte_trace_out(D_FILTERS, p, rt, "forced accept by protocol");
+       goto accept;
+     }
+   v = filter && ((filter == FILTER_REJECT) ||
+                (f_run(filter, &rt, tmpa, rte_update_pool, FF_FORCE_TMPATTR) > F_ACCEPT));
+   if (v)
+     {
+       if (silent)
+       goto reject;
+       stats->exp_updates_filtered++;
+       rte_trace_out(D_FILTERS, p, rt, "filtered out");
+       goto reject;
      }
  
-    * limit is breached and whether the update is new or not Therefore
+  accept:
+   if (rt != rt0)
+     *rt_free = rt;
+   return rt;
+  reject:
+   /* Discard temporary rte */
+   if (rt != rt0)
+     rte_free(rt);
+   return NULL;
+ }
+ static void
+ do_rt_notify(struct announce_hook *ah, net *net, rte *new, rte *old, ea_list *tmpa, int refeed)
+ {
+   struct proto *p = ah->proto;
+   struct proto_stats *stats = ah->stats;
++
 +  /*
++   * First, apply export limit.
++   *
 +   * Export route limits has several problems. Because exp_routes
 +   * counter is reset before refeed, we don't really know whether
-         if (new != new0)
-           rte_free(new);
++   * limit is breached and whether the update is new or not. Therefore
 +   * the number of really exported routes may exceed the limit
 +   * temporarily (routes exported before and new routes in refeed).
 +   *
 +   * Minor advantage is that if the limit is decreased and refeed is
 +   * requested, the number of exported routes really decrease.
 +   *
 +   * Second problem is that with export limits, we don't know whether
 +   * old was really exported (it might be blocked by limit). When a
 +   * withdraw is exported, we announce it even when the previous
 +   * update was blocked. This is not a big issue, but the same problem
 +   * is in updating exp_routes counter. Therefore, to be consistent in
 +   * increases and decreases of exp_routes, we count exported routes
 +   * regardless of blocking by limits.
 +   *
 +   * Similar problem is in handling updates - when a new route is
 +   * received and blocking is active, the route would be blocked, but
 +   * when an update for the route will be received later, the update
 +   * would be propagated (as old != NULL). Therefore, we have to block
 +   * also non-new updates (contrary to import blocking).
 +   */
 +
 +  struct proto_limit *l = ah->out_limit;
 +  if (l && new)
 +    {
 +      if ((!old || refeed) && (stats->exp_routes >= l->limit))
 +      proto_notify_limit(ah, l, stats->exp_routes);
 +
 +      if (l->state == PLS_BLOCKED)
 +      {
 +        stats->exp_routes++;  /* see note above */
 +        stats->exp_updates_rejected++;
 +        rte_trace_out(D_FILTERS, p, new, "rejected [limit]");
-   /* FIXME - This is broken because of incorrect 'old' value (see above) */
-   if (!new && !old)
-     return;
 +        new = NULL;
++
++        if (!old)
++          return;
 +      }
 +    }
 +
 +
    if (new)
      stats->exp_updates_accepted++;
    else
      }
    else
      p->rt_notify(p, ah->table, net, new, old, new->attrs->eattrs);
 -
+ }
 -
+ static void
+ rt_notify_basic(struct announce_hook *ah, net *net, rte *new, rte *old, ea_list *tmpa, int refeed)
+ {
+   // struct proto *p = ah->proto;
+   struct proto_stats *stats = ah->stats;
+   rte *new_free = NULL;
+   rte *old_free = NULL;
+   if (new)
+     stats->exp_updates_received++;
+   else
+     stats->exp_withdraws_received++;
+   /*
+    * This is a tricky part - we don't know whether route 'old' was
+    * exported to protocol 'p' or was filtered by the export filter.
+    * We try to run the export filter to know this to have a correct
+    * value in 'old' argument of rte_update (and proper filter value)
+    *
+    * FIXME - this is broken because 'configure soft' may change
+    * filters but keep routes. Refeed is expected to be called after
+    * change of the filters and with old == new, therefore we do not
+    * even try to run the filter on an old route, This may lead to 
+    * 'spurious withdraws' but ensure that there are no 'missing
+    * withdraws'.
+    *
+    * This is not completely safe as there is a window between
+    * reconfiguration and the end of refeed - if a newly filtered
+    * route disappears during this period, proper withdraw is not
+    * sent (because old would be also filtered) and the route is
+    * not refeeded (because it disappeared before that).
+    */
+   if (new)
+     new = export_filter(ah, new, &new_free, &tmpa, 0);
+   if (old && !refeed)
+     old = export_filter(ah, old, &old_free, NULL, 1);
+   /* FIXME - This is broken because of incorrect 'old' value (see above) */
+   if (!new && !old)
+     return;
+   do_rt_notify(ah, net, new, old, tmpa, refeed);
+   /* Discard temporary rte's */
+   if (new_free)
+     rte_free(new_free);
+   if (old_free)
+     rte_free(old_free);
+ }
+ static void
+ rt_notify_accepted(struct announce_hook *ah, net *net, rte *new_changed, rte *old_changed, rte *before_old,
+                  ea_list *tmpa, int feed)
+ {
+   // struct proto *p = ah->proto;
+   struct proto_stats *stats = ah->stats;
+   rte *new_best = NULL;
+   rte *old_best = NULL;
+   rte *new_free = NULL;
+   rte *old_free = NULL;
+   rte *r;
+   /* Used to track whether we met old_changed position. If it is NULL
+      it was the first and met it implicitly before current best route. */
+   int old_meet = (old_changed && !before_old) ? 1 : 0;
+   if (new_changed)
+     stats->exp_updates_received++;
+   else
+     stats->exp_withdraws_received++;
+   /* First, find the new_best route - first accepted by filters */
+   for (r=net->routes; r; r=r->next)
+     {
+       if (new_best = export_filter(ah, r, &new_free, &tmpa, 0))
+       break;
+       /* Note if we walked around the position of old_changed route */
+       if (r == before_old)
+       old_meet = 1;
+     }
+   /* 
+    * Second, handle the feed case. That means we do not care for
+    * old_best. It is NULL for feed, and the new_best for refeed. 
+    * For refeed, there is a hack similar to one in rt_notify_basic()
+    * to ensure withdraws in case of changed filters
+    */
+   if (feed)
+     {
+       if (feed == 2)  /* refeed */
+       old_best = new_best ? new_best : net->routes;
+       else
+       old_best = NULL;
+       if (!new_best && !old_best)
+       return;
+       goto found;
+     }
+   /*
+    * Now, we find the old_best route. Generally, it is the same as the
+    * new_best, unless new_best is the same as new_changed or
+    * old_changed is accepted before new_best.
+    *
+    * There are four cases:
+    *
+    * - We would find and accept old_changed before new_best, therefore
+    *   old_changed is old_best. In remaining cases we suppose this
+    *   is not true.
+    *
+    * - We found no new_best, therefore there is also no old_best and
+    *   we ignore this withdraw.
+    *
+    * - We found new_best different than new_changed, therefore
+    *   old_best is the same as new_best and we ignore this update.
+    *
+    * - We found new_best the same as new_changed, therefore it cannot
+    *   be old_best and we have to continue search for old_best.
+    */
+   /* First case */
+   if (old_meet)
+     if (old_best = export_filter(ah, old_changed, &old_free, NULL, 1))
+       goto found;
+   /* Second case */
+   if (!new_best)
+     return;
  
-   if (new && new != new0)     /* Discard temporary rte's */
-     rte_free(new);
-   if (old && old != old0)
-     rte_free(old);
+   /* Third case, we use r instead of new_best, because export_filter() could change it */
+   if (r != new_changed)
+     {
+       if (new_free)
+       rte_free(new_free);
+       return;
+     }
+   /* Fourth case */
+   for (r=r->next; r; r=r->next)
+     {
+       if (old_best = export_filter(ah, r, &old_free, NULL, 1))
+       goto found;
+       if (r == before_old)
+       if (old_best = export_filter(ah, old_changed, &old_free, NULL, 1))
+         goto found;
+     }
+   /* Implicitly, old_best is NULL and new_best is non-NULL */
+  found:
+   do_rt_notify(ah, net, new_best, old_best, tmpa, (feed == 2));
+   /* Discard temporary rte's */
+   if (new_free)
+     rte_free(new_free);
+   if (old_free)
+     rte_free(old_free);
  }
  
  /**
diff --cc proto/bgp/bgp.c
index 3b9f7cc5e57d3a50c874f7a58bc213596b57030e,d59b43080376d71cb7e792cc13a45a3c16a0622e..0b52dedc37ed621b089ad52e260b4f8f817a645a
@@@ -984,9 -972,15 +986,19 @@@ bgp_check_config(struct bgp_config *c
    if (!c->gw_mode)
      c->gw_mode = (c->multihop || internal) ? GW_RECURSIVE : GW_DIRECT;
  
 +  /* Disable after error incompatible with restart limit action */
 +  if (c->c.in_limit && (c->c.in_limit->action == PLA_RESTART) && c->disable_after_error)
 +    c->c.in_limit->action = PLA_DISABLE;
++
+   if ((c->gw_mode == GW_RECURSIVE) && c->c.table->sorted)
+     cf_error("BGP in recursive mode prohibits sorted table");
+   if (c->deterministic_med && c->c.table->sorted)
+     cf_error("BGP with deterministic MED prohibits sorted table");
+   if (c->secondary && !c->c.table->sorted)
+     cf_error("BGP with secondary option requires sorted table");
  }
  
  static int
diff --cc proto/bgp/bgp.h
index 1c16f4853591c2f1b4fcb2ed0f06201f3f299bed,87734425caf7c93c04554e2a29df14ef6b8682f4..c3adf254ad35df25d1abba43d2ad55550c06a2a0
@@@ -40,8 -40,10 +40,9 @@@ struct bgp_config 
    int rr_client;                      /* Whether neighbor is RR client of me */
    int rs_client;                      /* Whether neighbor is RS client of me */
    int advertise_ipv4;                 /* Whether we should add IPv4 capability advertisement to OPEN message */
 -  u32 route_limit;                    /* Number of routes that may be imported, 0 means disable limit */
    int passive;                                /* Do not initiate outgoing connection */
    int interpret_communities;          /* Hardwired handling of well-known communities */
+   int secondary;                      /* Accept also non-best routes (i.e. RA_ACCEPTED) */
    unsigned connect_retry_time;
    unsigned hold_time, initial_hold_time;
    unsigned keepalive_time;
index 5feaea0deb8167c5c443f3ac05630832365fd71c,f9a5be65e983fe68388c0d8d9cf63286269b949c..8b80d7fdc696979fdfc735f8b5bc99075babf62a
@@@ -98,13 -99,10 +99,14 @@@ bgp_proto
   | bgp_proto CAPABILITIES bool ';' { BGP_CFG->capabilities = $3; }
   | bgp_proto ADVERTISE IPV4 bool ';' { BGP_CFG->advertise_ipv4 = $4; }
   | bgp_proto PASSWORD TEXT ';' { BGP_CFG->password = $3; }
 - | bgp_proto ROUTE LIMIT expr ';' { BGP_CFG->route_limit = $4; }
 + | bgp_proto ROUTE LIMIT expr ';' {
 +     this_proto->in_limit = cfg_allocz(sizeof(struct proto_limit));
 +     this_proto->in_limit->limit = $4;
 +     this_proto->in_limit->action = PLA_RESTART;
 +   }
   | bgp_proto PASSIVE bool ';' { BGP_CFG->passive = $3; }
   | bgp_proto INTERPRET COMMUNITIES bool ';' { BGP_CFG->interpret_communities = $4; }
+  | bgp_proto SECONDARY bool ';' { BGP_CFG->secondary = $3; }
   | bgp_proto IGP TABLE rtable ';' { BGP_CFG->igp_table = $4; }
   | bgp_proto TTL SECURITY bool ';' { BGP_CFG->ttl_security = $4; }
   ;