]> git.ipfire.org Git - thirdparty/bird.git/commitdiff
Separate import and receive limits.
authorOndrej Zajicek <santiago@crfreenet.org>
Thu, 10 Jan 2013 12:07:33 +0000 (13:07 +0100)
committerOndrej Zajicek <santiago@crfreenet.org>
Thu, 10 Jan 2013 12:07:33 +0000 (13:07 +0100)
They have different behavior w.r.t. filtered routes that are kept.

README
doc/bird.sgml
nest/config.Y
nest/proto.c
nest/protocol.h
nest/rt-table.c
proto/bgp/bgp.c
proto/pipe/pipe.c

diff --git a/README b/README
index 5c2ef07626434cb102e969bbe62b0883b8aa93e3..daeb18bd48a97a65ef0db86ab46dc3cb51494e53 100644 (file)
--- a/README
+++ b/README
@@ -3,7 +3,7 @@
                (c) 1998--2008  Martin Mares <mj@ucw.cz>
                 (c) 1998--2000  Pavel Machek <pavel@ucw.cz>
                 (c) 1998--2008  Ondrej Filip <feela@network.cz>
-                (c) 2009--2011  CZ.NIC z.s.p.o.
+                (c) 2009--2013  CZ.NIC z.s.p.o.
 
 ================================================================================
 
index 4e04a13881075c9784a4d915b7702c6d755e7c0f..b0d4e6a1dc8419117dd78c24022b08481696bff9 100644 (file)
@@ -482,15 +482,23 @@ to zero to disable it. An empty <cf><m/switch/</cf> is equivalent to <cf/on/
        Specify an import route limit (a maximum number of routes
        imported from the protocol) and optionally the action to be
        taken when the limit is hit. Warn action just prints warning
-       log message. Block action ignores new routes coming from the
+       log message. Block action discards new routes coming from the
        protocol. Restart and disable actions shut the protocol down
        like appropriate commands. Disable is the default action if an
        action is not explicitly specified. Note that limits are reset
-       during protocol reconfigure, reload or restart. Also note that
-       if <cf/import keep filtered/ is active, filtered routes are
-       counted towards the limit and blocked routes are forgotten, as
-       the main purpose of the import limit is to protect routing
-       tables from overflow. Default: <cf/none/.
+       during protocol reconfigure, reload or restart. Default: <cf/none/.
+
+       <tag>receive limit <m/number/ [action warn | block | restart | disable]</tag>
+       Specify an receive route limit (a maximum number of routes
+       received from the protocol and remembered). It works almost
+       identically to <cf>import limit</cf> option, the only
+       difference is that if <cf/import keep filtered/ option is
+       active, filtered routes are counted towards the limit and
+       blocked routes are forgotten, as the main purpose of the
+       receive limit is to protect routing tables from
+       overflow. Import limit, on the contrary, counts accepted
+       routes only and routes blocked by the limit are handled like
+       filtered routes. Default: <cf/none/.
 
        <tag>export limit <m/number/ [action warn | block | restart | disable]</tag>
        Specify an export route limit, works similarly to
index dbd7205560b98a18ce8e00bdf128a709d465b444..e46b5fb555def5f6922a5513f70212d8f092983a 100644 (file)
@@ -44,7 +44,7 @@ CF_DECLS
 
 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, KEEP, FILTERED)
+CF_KEYWORDS(RECEIVE, LIMIT, ACTION, WARN, BLOCK, RESTART, DISABLE, KEEP, FILTERED)
 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, SORTED)
@@ -185,6 +185,7 @@ proto_item:
  | MRTDUMP mrtdump_mask { this_proto->mrtdump = $2; }
  | IMPORT imexport { this_proto->in_filter = $2; }
  | EXPORT imexport { this_proto->out_filter = $2; }
+ | RECEIVE LIMIT limit_spec { this_proto->rx_limit = $3; }
  | IMPORT LIMIT limit_spec { this_proto->in_limit = $3; }
  | EXPORT LIMIT limit_spec { this_proto->out_limit = $3; }
  | IMPORT KEEP FILTERED bool { this_proto->in_keep_filtered = $4; }
index b976a6cbcb03a19c9b14e8c2d791ee1b9792798f..7e7fb7fa4fce0457d07a0937572c3ff6c9f544fa 100644 (file)
@@ -344,6 +344,7 @@ protos_postconfig(struct config *c)
   WALK_LIST(x, c->protos)
     {
       DBG(" %s", x->name);
+
       p = x->protocol;
       if (p->postconfig)
        p->postconfig(x);
@@ -410,6 +411,7 @@ proto_reconfigure(struct proto *p, struct proto_config *oc, struct proto_config
     {
       p->main_ahook->in_filter = nc->in_filter;
       p->main_ahook->out_filter = nc->out_filter;
+      p->main_ahook->rx_limit = nc->rx_limit;
       p->main_ahook->in_limit = nc->in_limit;
       p->main_ahook->out_limit = nc->out_limit;
       p->main_ahook->in_keep_filtered = nc->in_keep_filtered;
@@ -804,9 +806,11 @@ proto_schedule_feed(struct proto *p, int initial)
       p->main_ahook = proto_add_announce_hook(p, p->table, &p->stats);
       p->main_ahook->in_filter = p->cf->in_filter;
       p->main_ahook->out_filter = p->cf->out_filter;
+      p->main_ahook->rx_limit = p->cf->rx_limit;
       p->main_ahook->in_limit = p->cf->in_limit;
       p->main_ahook->out_limit = p->cf->out_limit;
       p->main_ahook->in_keep_filtered = p->cf->in_keep_filtered;
+      proto_reset_limit(p->main_ahook->rx_limit);
       proto_reset_limit(p->main_ahook->in_limit);
       proto_reset_limit(p->main_ahook->out_limit);
     }
@@ -978,6 +982,7 @@ proto_limit_name(struct proto_limit *l)
  * proto_notify_limit: notify about limit hit and take appropriate action
  * @ah: announce hook
  * @l: limit being hit
+ * @dir: limit direction (PLD_*)
  * @rt_count: the number of routes 
  *
  * The function is called by the route processing core when limit @l
@@ -985,10 +990,11 @@ proto_limit_name(struct proto_limit *l)
  * according to @l->action.
  */
 void
-proto_notify_limit(struct announce_hook *ah, struct proto_limit *l, u32 rt_count)
+proto_notify_limit(struct announce_hook *ah, struct proto_limit *l, int dir, u32 rt_count)
 {
+  const char *dir_name[PLD_MAX] = { "receive", "import" , "export" };
+  const byte dir_down[PLD_MAX] = { PDC_RX_LIMIT_HIT, PDC_IN_LIMIT_HIT, PDC_OUT_LIMIT_HIT };
   struct proto *p = ah->proto;
-  int dir = (ah->in_limit == l);
 
   if (l->state == PLS_BLOCKED)
     return;
@@ -996,7 +1002,7 @@ proto_notify_limit(struct announce_hook *ah, struct proto_limit *l, u32 rt_count
   /* For warning action, we want the log message every time we hit the limit */
   if (!l->state || ((l->action == PLA_WARN) && (rt_count == l->limit)))
     log(L_WARN "Protocol %s hits route %s limit (%d), action: %s",
-       p->name, dir ? "import" : "export", l->limit, proto_limit_name(l));
+       p->name, dir_name[dir], l->limit, proto_limit_name(l));
 
   switch (l->action)
     {
@@ -1011,8 +1017,7 @@ proto_notify_limit(struct announce_hook *ah, struct proto_limit *l, u32 rt_count
     case PLA_RESTART:
     case PLA_DISABLE:
       l->state = PLS_BLOCKED;
-      proto_schedule_down(p, l->action == PLA_RESTART,
-                         dir ? PDC_IN_LIMIT_HIT : PDC_OUT_LIMIT_HIT);
+      proto_schedule_down(p, l->action == PLA_RESTART, dir_down[dir]);
       break;
     }
 }
@@ -1146,6 +1151,7 @@ proto_show_basic_info(struct proto *p)
   cli_msg(-1006, "  Input filter:   %s", filter_name(p->cf->in_filter));
   cli_msg(-1006, "  Output filter:  %s", filter_name(p->cf->out_filter));
 
+  proto_show_limit(p->cf->rx_limit, "Receive limit:");
   proto_show_limit(p->cf->in_limit, "Import limit:");
   proto_show_limit(p->cf->out_limit, "Export limit:");
 
@@ -1267,7 +1273,10 @@ proto_cmd_reload(struct proto *p, unsigned int dir, int cnt UNUSED)
        * Perhaps, but these hooks work asynchronously.
        */
       if (!p->proto->multitable)
-       proto_reset_limit(p->main_ahook->in_limit);
+       {
+         proto_reset_limit(p->main_ahook->rx_limit);
+         proto_reset_limit(p->main_ahook->in_limit);
+       }
     }
 
   /* re-exporting routes */
index cf2ca0a4fd2e22830cab4f226c120028eb6cf950..033a0ede4632bbd7136866564e463565b9403d50 100644 (file)
@@ -95,6 +95,8 @@ struct proto_config {
   u32 router_id;                       /* Protocol specific router ID */
   struct rtable_config *table;         /* Table we're attached to */
   struct filter *in_filter, *out_filter; /* Attached filters */
+  struct proto_limit *rx_limit;                /* Limit for receiving routes from protocol
+                                          (relevant when in_keep_filtered is active) */
   struct proto_limit *in_limit;                /* Limit for importing routes from protocol */
   struct proto_limit *out_limit;       /* Limit for exporting routes to protocol */
 
@@ -225,8 +227,9 @@ struct proto_spec {
 #define PDC_CMD_DISABLE                0x11    /* Result of disable command */
 #define PDC_CMD_RESTART                0x12    /* Result of restart command */
 #define PDC_CMD_SHUTDOWN       0x13    /* Result of global shutdown */
-#define PDC_IN_LIMIT_HIT       0x21    /* Route import limit reached */
-#define PDC_OUT_LIMIT_HIT      0x22    /* Route export limit reached */
+#define PDC_RX_LIMIT_HIT       0x21    /* Route receive limit reached */
+#define PDC_IN_LIMIT_HIT       0x22    /* Route import limit reached */
+#define PDC_OUT_LIMIT_HIT      0x23    /* Route export limit reached */
 
 
 void *proto_new(struct proto_config *, unsigned size);
@@ -373,6 +376,11 @@ extern struct proto_config *cf_dev_proto;
  * Protocol limits
  */
 
+#define PLD_RX         0       /* Receive limit */
+#define PLD_IN         1       /* Import limit */
+#define PLD_OUT                2       /* Export limit */
+#define PLD_MAX                3
+
 #define PLA_WARN       1       /* Issue log warning */
 #define PLA_BLOCK      2       /* Block new routes */
 #define PLA_RESTART    4       /* Force protocol restart */
@@ -388,7 +396,7 @@ struct proto_limit {
   byte state;                  /* State of limit (PLS_*) */
 };
 
-void proto_notify_limit(struct announce_hook *ah, struct proto_limit *l, u32 rt_count);
+void proto_notify_limit(struct announce_hook *ah, struct proto_limit *l, int dir, u32 rt_count);
 
 static inline void
 proto_reset_limit(struct proto_limit *l)
@@ -408,6 +416,7 @@ struct announce_hook {
   struct proto *proto;
   struct filter *in_filter;            /* Input filter */
   struct filter *out_filter;           /* Output filter */
+  struct proto_limit *rx_limit;                /* Receive limit (for in_keep_filtered) */
   struct proto_limit *in_limit;                /* Input limit */
   struct proto_limit *out_limit;       /* Output limit */
   struct proto_stats *stats;           /* Per-table protocol statistics */
index 2f0840f0562c745d2b0aedef4a3fe6d37b72c2a0..99175448c61359fd5820b0d873da973a88f9799d 100644 (file)
@@ -285,7 +285,7 @@ do_rt_notify(struct announce_hook *ah, net *net, rte *new, rte *old, ea_list *tm
   if (l && new)
     {
       if ((!old || refeed) && (stats->exp_routes >= l->limit))
-       proto_notify_limit(ah, l, stats->exp_routes);
+       proto_notify_limit(ah, l, PLD_OUT, stats->exp_routes);
 
       if (l->state == PLS_BLOCKED)
        {
@@ -700,16 +700,22 @@ rte_recalculate(struct announce_hook *ah, net *net, rte *new, ea_list *tmpa, str
       return;
     }
 
-  struct proto_limit *l = ah->in_limit;
+  int new_ok = rte_is_ok(new);
+  int old_ok = rte_is_ok(old);
+
+  struct proto_limit *l = ah->rx_limit;
   if (l && !old && new)
     {
       u32 all_routes = stats->imp_routes + stats->filt_routes;
 
       if (all_routes >= l->limit)
-       proto_notify_limit(ah, l, all_routes);
+       proto_notify_limit(ah, l, PLD_RX, all_routes);
 
       if (l->state == PLS_BLOCKED)
        {
+         /* In receive limit the situation is simple, old is NULL so
+            we just free new and exit like nothing happened */
+
          stats->imp_updates_ignored++;
          rte_trace_in(D_FILTERS, p, new, "ignored [limit]");
          rte_free_quick(new);
@@ -717,8 +723,39 @@ rte_recalculate(struct announce_hook *ah, net *net, rte *new, ea_list *tmpa, str
        }
     }
 
-  int new_ok = rte_is_ok(new);
-  int old_ok = rte_is_ok(old);
+  l = ah->in_limit;
+  if (l && !old_ok && new_ok)
+    {
+      if (stats->imp_routes >= l->limit)
+       proto_notify_limit(ah, l, PLD_IN, stats->imp_routes);
+
+      if (l->state == PLS_BLOCKED)
+       {
+         /* In import limit the situation is more complicated. We
+            shouldn't just drop the route, we should handle it like
+            it was filtered. We also have to continue the route
+            processing if old or new is non-NULL, but we should exit
+            if both are NULL as this case is probably assumed to be
+            already handled. */
+
+         stats->imp_updates_ignored++;
+         rte_trace_in(D_FILTERS, p, new, "ignored [limit]");
+
+         if (ah->in_keep_filtered)
+           new->flags |= REF_FILTERED;
+         else
+           { rte_free_quick(new); new = NULL; }
+
+         /* Note that old && !new could be possible when
+            ah->in_keep_filtered changed in the recent past. */
+
+         if (!old && !new)
+           return;
+
+         new_ok = 0;
+         goto skip_stats1;
+       }
+    }
 
   if (new_ok)
     stats->imp_updates_accepted++;
@@ -727,6 +764,8 @@ rte_recalculate(struct announce_hook *ah, net *net, rte *new, ea_list *tmpa, str
   else
     stats->imp_withdraws_ignored++;
 
+ skip_stats1:
+
   if (new)
     rte_is_filtered(new) ? stats->filt_routes++ : stats->imp_routes++;
   if (old)
index 249d2e07e8524386d8034ecc51fd3c84b4f45b67..0f351b44a68d3a6073de14d49f1268ec484a636c 100644 (file)
@@ -878,6 +878,7 @@ bgp_shutdown(struct proto *P)
       subcode = 4; // Errcode 6, 4 - administrative reset
       break;
 
+    case PDC_RX_LIMIT_HIT:
     case PDC_IN_LIMIT_HIT:
       subcode = 1; // Errcode 6, 1 - max number of prefixes reached
       /* log message for compatibility */
index 6099d2846ca18ffa5baec0e348a399484f3d339a..51be3c7d358bcdd17b64965dc3916aed7d05885b 100644 (file)
@@ -200,6 +200,11 @@ pipe_postconfig(struct proto_config *C)
     cf_error("Name of peer routing table not specified");
   if (c->peer == C->table)
     cf_error("Primary table and peer table must be different");
+
+  if (C->in_keep_filtered)
+    cf_error("Pipe protocol prohibits keeping filtered routes");
+  if (C->rx_limit)
+    cf_error("Pipe protocol does not support receive limits");
 }
 
 extern int proto_reconfig_type;