]> git.ipfire.org Git - thirdparty/bird.git/commitdiff
Implements generalized import hooks.
authorOndrej Zajicek <santiago@crfreenet.org>
Sun, 15 Apr 2012 13:28:29 +0000 (15:28 +0200)
committerOndrej Zajicek <santiago@crfreenet.org>
Sun, 15 Apr 2012 13:28:29 +0000 (15:28 +0200)
Thanks to Alexander V. Chernikov for the original patch.

13 files changed:
doc/bird.sgml
nest/config.Y
nest/proto.c
nest/protocol.h
nest/rt-table.c
proto/bgp/bgp.c
proto/bgp/bgp.h
proto/bgp/config.Y
proto/bgp/packets.c
proto/pipe/config.Y
proto/pipe/pipe.c
proto/pipe/pipe.h
sysdep/unix/timer.h

index 20738be3802173b519ad38db4b8d4c6eed74aabb..6f96b8624b6a9336b6856abc5a4c18fc22b95586 100644 (file)
@@ -431,6 +431,14 @@ to zero to disable it. An empty <cf><m/switch/</cf> is equivalent to <cf/on/
        <tag>export <m/filter/</tag> This is similar to the <cf>import</cf> keyword, except that it
        works in the direction from the routing table to the protocol. Default: <cf/none/.
 
+       <tag>import limit <m/number/ exceed warn | block | restart | disable</tag>
+       Specify an import route limit and the action to be taken when
+       the limit is hit. Warn action just prints warning log
+       message. Block action ignores new routes (and converts route
+       updates to withdraws) coming from the protocol. Restart and
+       disable actions shut the protocol down like appropriate
+       commands. Default: <cf/none/.
+
        <tag>description "<m/text/"</tag> This is an optional
        description of the protocol. It is displayed as a part of the
        output of 'show route all' command.
@@ -1327,7 +1335,8 @@ for each neighbor using the following configuration parameters:
 
        <tag>route limit <m/number/</tag> The maximal number of routes
        that may be imported from the protocol. If the route limit is
-       exceeded, the connection is closed with error. Default: no limit.
+       exceeded, the connection is closed with error. Limit is currently implemented as
+       <cf/import limit number exceed restart/. Default: no limit.
 
        <tag>disable after error <m/switch/</tag> When an error is encountered (either
        locally or by the other side), disable the instance automatically
index f889828afc640dd97fc210bdffb629d79a41f959..60b03278ad3ff3877df6e91d583c486b3bc9bbf3 100644 (file)
@@ -44,6 +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(EXCEED, LIMIT, 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)
@@ -64,8 +65,9 @@ CF_ENUM(T_ENUM_ROA, ROA_, UNKNOWN, VALID, INVALID)
 %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
+%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 <ps> proto_patt proto_patt2
+%type <g> limit_spec
 
 CF_GRAMMAR
 
@@ -176,6 +178,7 @@ proto_item:
  | MRTDUMP mrtdump_mask { this_proto->mrtdump = $2; }
  | IMPORT imexport { this_proto->in_filter = $2; }
  | EXPORT imexport { this_proto->out_filter = $2; }
+ | IMPORT LIMIT limit_spec { this_proto->in_limit = $3; }
  | TABLE rtable { this_proto->table = $2; }
  | ROUTER ID idval { this_proto->router_id = $3; }
  | DESCRIPTION TEXT { this_proto->dsc = $2; }
@@ -188,6 +191,22 @@ imexport:
  | NONE { $$ = FILTER_REJECT; }
  ;
 
+limit_action:
+   WARN { $$ = PLA_WARN; }
+ | BLOCK { $$ = PLA_BLOCK; }
+ | RESTART { $$ = PLA_RESTART; }
+ | DISABLE { $$ = PLA_DISABLE; }
+ ;
+
+limit_spec:
+   expr EXCEED limit_action {
+     struct proto_limit *l = cfg_allocz(sizeof(struct proto_limit));
+     l->limit = $1;
+     l->action = $3;
+     $$ = l;
+   }
+ ;
+
 rtable:
    SYM {
      if ($1->class != SYM_TABLE) cf_error("Table name expected");
index 802d5238a14c2c88115e9866071ee2e9771e7390..418a7a61cb88e5fae8e901eda00faee50e3362a6 100644 (file)
@@ -34,11 +34,13 @@ static list flush_proto_list;
 static struct proto *initial_device_proto;
 
 static event *proto_flush_event;
+static timer *proto_shutdown_timer;
 
 static char *p_states[] = { "DOWN", "START", "UP", "STOP" };
 static char *c_states[] = { "HUNGRY", "FEEDING", "HAPPY", "FLUSHING" };
 
 static void proto_flush_loop(void *);
+static void proto_shutdown_loop(struct timer *);
 static void proto_rethink_goal(struct proto *p);
 static char *proto_state_name(struct proto *p);
 
@@ -134,8 +136,6 @@ extern pool *rt_table_pool;
  * proto_add_announce_hook - connect protocol to a routing table
  * @p: protocol instance
  * @t: routing table to connect to
- * @in: input filter
- * @out: output filter
  * @stats: per-table protocol statistics
  *
  * This function creates a connection between the protocol instance @p
@@ -155,8 +155,7 @@ extern pool *rt_table_pool;
  * automatically by the core code.
  */
 struct announce_hook *
-proto_add_announce_hook(struct proto *p, struct rtable *t, struct filter *in,
-                       struct filter *out, struct proto_stats *stats)
+proto_add_announce_hook(struct proto *p, struct rtable *t, struct proto_stats *stats)
 {
   struct announce_hook *h;
 
@@ -166,8 +165,6 @@ proto_add_announce_hook(struct proto *p, struct rtable *t, struct filter *in,
   h = mb_allocz(rt_table_pool, sizeof(struct announce_hook));
   h->table = t;
   h->proto = p;
-  h->in_filter = in;
-  h->out_filter = out;
   h->stats = stats;
 
   h->next = p->ahooks;
@@ -414,6 +411,8 @@ 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->in_limit = nc->in_limit;
+      // p->main_ahook->out_limit = nc->out_limit;
     }
 
   /* Update routes when filters changed. If the protocol in not UP,
@@ -438,6 +437,7 @@ proto_reconfigure(struct proto *p, struct proto_config *oc, struct proto_config
         and we have to do regular protocol restart. */
       log(L_INFO "Restarting protocol %s", p->name);
       p->disabled = 1;
+      p->down_code = PDC_CF_RESTART;
       proto_rethink_goal(p);
       p->disabled = 0;
       proto_rethink_goal(p);
@@ -512,6 +512,7 @@ protos_commit(struct config *new, struct config *old, int force_reconfig, int ty
                log(L_INFO "Disabling protocol %s", p->name);
 
              PD(p, "Restarting");
+             p->down_code = nc->disabled ? PDC_CF_DISABLE : PDC_CF_RESTART;
              p->cf_new = nc;
            }
          else
@@ -519,9 +520,11 @@ protos_commit(struct config *new, struct config *old, int force_reconfig, int ty
              if (!shutting_down)
                log(L_INFO "Removing protocol %s", p->name);
              PD(p, "Unconfigured");
+             p->down_code = PDC_CF_REMOVE;
              p->cf_new = NULL;
            }
          p->reconfiguring = 1;
+
          config_add_obstacle(old);
          proto_rethink_goal(p);
        }
@@ -704,6 +707,8 @@ protos_build(void)
   proto_pool = rp_new(&root_pool, "Protocols");
   proto_flush_event = ev_new(proto_pool);
   proto_flush_event->hook = proto_flush_loop;
+  proto_shutdown_timer = tm_new(proto_pool);
+  proto_shutdown_timer->hook = proto_shutdown_loop;
 }
 
 static void
@@ -775,8 +780,13 @@ proto_schedule_feed(struct proto *p, int initial)
 
   /* Connect protocol to routing table */
   if (initial && !p->proto->multitable)
-    p->main_ahook = proto_add_announce_hook(p, p->table,
-      p->cf->in_filter, p->cf->out_filter, &p->stats);
+    {
+      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->in_limit = p->cf->in_limit;
+      // p->main_ahook->out_limit = p->cf->out_limit;
+    }
 
   proto_relink(p);
   p->attn->hook = initial ? proto_feed_initial : proto_feed_more;
@@ -861,6 +871,42 @@ proto_schedule_flush(struct proto *p)
 }
 
 
+static void
+proto_shutdown_loop(struct timer *t UNUSED)
+{
+  struct proto *p, *p_next;
+
+  WALK_LIST_DELSAFE(p, p_next, active_proto_list)
+    if (p->down_sched)
+      {
+       int restart = (p->down_sched == PDS_RESTART);
+
+       p->disabled = 1;
+       proto_rethink_goal(p);
+       if (restart)
+         {
+           p->disabled = 0;
+           proto_rethink_goal(p);
+         }
+      }
+}
+
+static inline void
+proto_schedule_down(struct proto *p, byte restart, byte code)
+{
+  /* Does not work for other states (even PS_START) */
+  ASSERT(p->proto_state == PS_UP);
+
+  /* Scheduled restart may change to shutdown, but not otherwise */
+  if (p->down_sched == PDS_DISABLE)
+    return;
+
+  p->down_sched = restart ? PDS_RESTART : PDS_DISABLE;
+  p->down_code = code;
+  tm_start_max(proto_shutdown_timer, restart ? 2 : 0);
+}
+
+
 /**
  * proto_request_feeding - request feeding routes to the protocol
  * @p: given protocol 
@@ -890,6 +936,62 @@ proto_request_feeding(struct proto *p)
   proto_schedule_feed(p, 0);
 }
 
+static const char *
+proto_limit_name(struct proto_limit *l)
+{
+  const char *actions[] = {
+    [PLA_WARN] = "warn",
+    [PLA_BLOCK] = "block",
+    [PLA_RESTART] = "restart",
+    [PLA_DISABLE] = "disable",
+  };
+
+  return actions[l->action];
+}
+
+/**
+ * proto_notify_limit: notify about limit hit and take appropriate action
+ * @ah: announce hook
+ * @l: limit being hit
+ *
+ * The function is called by the route processing core when limit @l
+ * is breached. It activates the limit and tooks appropriate action
+ * according to @l->action. It also says what should be done with the
+ * route that breached the limit.
+ *
+ * Returns 1 if the route should be freed, 0 otherwise.
+ */
+int
+proto_notify_limit(struct announce_hook *ah, struct proto_limit *l)
+{
+  struct proto *p = ah->proto;
+  int dir = (ah->in_limit == l);
+
+  if (l->active)
+    return (l->action != PLA_WARN);
+
+  l->active = 1;
+  log(L_WARN "Protocol %s hits route %s limit (%d), action: %s",
+      p->name, dir ? "import" : "export", l->limit, proto_limit_name(l));
+
+  switch (l->action)
+    {
+    case PLA_WARN:
+      return 0;
+
+    case PLA_BLOCK:
+      return 1;
+
+    case PLA_RESTART:
+    case PLA_DISABLE:
+      proto_schedule_down(p, l->action == PLA_RESTART,
+                         dir ? PDC_IN_LIMIT_HIT : PDC_OUT_LIMIT_HIT);
+      return 1;
+    }
+
+  return 0;
+}
+
 /**
  * proto_notify_state - notify core about protocol state change
  * @p: protocol the state of which has changed
@@ -919,6 +1021,8 @@ proto_notify_state(struct proto *p, unsigned ps)
   switch (ps)
     {
     case PS_DOWN:
+      p->down_code = 0;
+      p->down_sched = 0;
       if ((cs == FS_FEEDING) || (cs == FS_HAPPY))
        proto_schedule_flush(p);
 
@@ -942,6 +1046,7 @@ proto_notify_state(struct proto *p, unsigned ps)
       proto_schedule_feed(p, 1);
       break;
     case PS_STOP:
+      p->down_sched = 0;
       if ((cs == FS_FEEDING) || (cs == FS_HAPPY))
        proto_schedule_flush(p);
       break;
@@ -993,6 +1098,14 @@ proto_show_stats(struct proto_stats *s)
          s->exp_withdraws_received, s->exp_withdraws_accepted);
 }
 
+void
+proto_show_limit(struct proto_limit *l, const char *dsc)
+{
+  if (l)
+    cli_msg(-1006, "  %16s%d, action: %s%s", dsc, l->limit,
+           proto_limit_name(l), l->active ? " [HIT]" : "");
+}
+
 void
 proto_show_basic_info(struct proto *p)
 {
@@ -1001,6 +1114,8 @@ 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->in_limit, "Import limit:");
+
   if (p->proto_state != PS_DOWN)
     proto_show_stats(&p->stats);
 }
@@ -1052,6 +1167,7 @@ proto_cmd_disable(struct proto *p, unsigned int arg UNUSED, int cnt UNUSED)
 
   log(L_INFO "Disabling protocol %s", p->name);
   p->disabled = 1;
+  p->down_code = PDC_CMD_DISABLE;
   proto_rethink_goal(p);
   cli_msg(-9, "%s: disabled", p->name);
 }
@@ -1082,6 +1198,7 @@ proto_cmd_restart(struct proto *p, unsigned int arg UNUSED, int cnt UNUSED)
 
   log(L_INFO "Restarting protocol %s", p->name);
   p->disabled = 1;
+  p->down_code = PDC_CMD_RESTART;
   proto_rethink_goal(p);
   p->disabled = 0;
   proto_rethink_goal(p);
@@ -1105,12 +1222,21 @@ proto_cmd_reload(struct proto *p, unsigned int dir, int cnt UNUSED)
 
   /* re-importing routes */
   if (dir != CMD_RELOAD_OUT)
-    if (! (p->reload_routes && p->reload_routes(p)))
-      {
-       cli_msg(-8006, "%s: reload failed", p->name);
-       return;
-      }
-                
+    {
+      if (! (p->reload_routes && p->reload_routes(p)))
+       {
+         cli_msg(-8006, "%s: reload failed", p->name);
+         return;
+       }
+
+      /*
+       * Should be done before reload_routes() hook?
+       * Perhaps, but these hooks work asynchronously.
+       */
+      if (!p->proto->multitable && p->main_ahook->in_limit)
+       p->main_ahook->in_limit->active = 0;
+    }
+
   /* re-exporting routes */
   if (dir != CMD_RELOAD_IN)
     proto_request_feeding(p);
index 983ce75a353328ed0b9e10f657de9d6617508d7e..3dd7cf2fffc5ec3e18bc74eca39e93ff43ddd954 100644 (file)
@@ -94,13 +94,15 @@ 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 *in_limit;                /* Limit for importing routes from protocol */
+  // struct proto_limit *out_limit;    /* Limit for exporting routes to protocol */
 
   /* Check proto_reconfigure() and proto_copy_config() after changing struct proto_config */
 
   /* Protocol-specific data follow... */
 };
 
-  /* Protocol statistics */
+/* Protocol statistics */
 struct proto_stats {
   /* Import - from protocol to core */
   u32 imp_routes;              /* Number of routes successfully imported to the (adjacent) routing table */
@@ -138,14 +140,16 @@ struct proto {
   u32 debug;                           /* Debugging flags */
   u32 mrtdump;                         /* MRTDump flags */
   unsigned preference;                 /* Default route preference */
-  unsigned accept_ra_types;            /* Which types of route announcements are accepted (RA_OPTIMAL or RA_ANY) */
-  unsigned disabled;                   /* Manually disabled */
-  unsigned proto_state;                        /* Protocol state machine (see below) */
-  unsigned core_state;                 /* Core state machine (see below) */
-  unsigned core_goal;                  /* State we want to reach (see below) */
-  unsigned reconfiguring;              /* We're shutting down due to reconfiguration */
-  unsigned refeeding;                  /* We are refeeding (valid only if core_state == FS_FEEDING) */
-  unsigned flushing;                   /* Protocol is flushed in current flush loop round */
+  byte accept_ra_types;                        /* Which types of route announcements are accepted (RA_OPTIMAL or RA_ANY) */
+  byte disabled;                       /* Manually disabled */
+  byte proto_state;                    /* Protocol state machine (PS_*, see below) */
+  byte core_state;                     /* Core state machine (FS_*, see below) */
+  byte core_goal;                      /* State we want to reach (FS_*, see below) */
+  byte reconfiguring;                  /* We're shutting down due to reconfiguration */
+  byte refeeding;                      /* We are refeeding (valid only if core_state == FS_FEEDING) */
+  byte flushing;                       /* Protocol is flushed in current flush loop round */
+  byte down_sched;                     /* Shutdown is scheduled for later (PDS_*) */
+  byte down_code;                      /* Reason for shutdown (PDC_* codes) */
   u32 hash_key;                                /* Random key used for hashing of neighbors */
   bird_clock_t last_state_change;      /* Time of last state transition */
   char *last_state_name_announced;     /* Last state name we've announced to the user */
@@ -210,6 +214,18 @@ struct proto_spec {
 };
 
 
+#define PDS_DISABLE            1       /* Proto disable scheduled */
+#define PDS_RESTART            2       /* Proto restart scheduled */
+
+#define PDC_CF_REMOVE          0x01    /* Removed in new config */
+#define PDC_CF_DISABLE         0x02    /* Disabled in new config */
+#define PDC_CF_RESTART         0x03    /* Restart due to reconfiguration */
+#define PDC_CMD_DISABLE                0x11    /* Result of disable command */
+#define PDC_CMD_RESTART                0x12    /* Result of restart command */
+#define PDC_IN_LIMIT_HIT       0x21    /* Route import limit reached */
+#define PDC_OUT_LIMIT_HIT      0x22    /* Route export limit reached - not implemented */
+
+
 void *proto_new(struct proto_config *, unsigned size);
 void *proto_config_new(struct protocol *, unsigned size, int class);
 void proto_copy_config(struct proto_config *dest, struct proto_config *src);
@@ -220,6 +236,7 @@ proto_copy_rest(struct proto_config *dest, struct proto_config *src, unsigned si
 { memcpy(dest + 1, src + 1, size - sizeof(struct proto_config)); }
 
 
+void proto_show_limit(struct proto_limit *l, const char *dsc);
 void proto_show_basic_info(struct proto *p);
 
 void proto_cmd_show(struct proto *, unsigned int, int);
@@ -348,6 +365,24 @@ void proto_notify_state(struct proto *p, unsigned state);
 
 extern struct proto_config *cf_dev_proto;
 
+
+/*
+ * Protocol limits
+ */
+
+#define PLA_WARN       1       /* Issue log warning */
+#define PLA_BLOCK      2       /* Block new routes */
+#define PLA_RESTART    4       /* Force protocol restart */
+#define PLA_DISABLE    5       /* Shutdown and disable protocol */
+
+struct proto_limit {
+  u32 limit;                   /* Maximum number of prefixes */
+  byte action;                 /* Action to take (PLA_*) */
+  byte active;                 /* Limit is active */
+};
+
+int proto_notify_limit(struct announce_hook *ah, struct proto_limit *l);
 /*
  *     Route Announcement Hook
  */
@@ -358,11 +393,13 @@ struct announce_hook {
   struct proto *proto;
   struct filter *in_filter;            /* Input filter */
   struct filter *out_filter;           /* Output filter */
+  struct proto_limit *in_limit;                /* Input limit */
+  // struct proto_limit *out_limit;    /* Output limit */
   struct proto_stats *stats;           /* Per-table protocol statistics */
   struct announce_hook *next;          /* Next hook for the same protocol */
 };
 
-struct announce_hook *proto_add_announce_hook(struct proto *, struct rtable *, struct filter *, struct filter *, struct proto_stats *);
+struct announce_hook *proto_add_announce_hook(struct proto *p, struct rtable *t, struct proto_stats *stats);
 struct announce_hook *proto_find_announce_hook(struct proto *p, struct rtable *t);
 
 #endif
index 310c1afdadf0b4f8acc6334516d281c7669868db..6a28fd43dff87a33d12dd9d52a33da519454116b 100644 (file)
@@ -182,6 +182,16 @@ rte_trace_out(unsigned int flag, struct proto *p, rte *e, char *msg)
     rte_trace(p, e, '<', msg);
 }
 
+/**
+ * do_rte_announce - announce new rte to protocol
+ * @ah: pointer to announce hook
+ * @type: announce type (RA_ANY or RA_OPTIMAL)
+ * @net: pointer to announced network
+ * @new: new rte or NULL
+ * @old: previous rte or NULL
+ * @tmpa: new rte attributes (possibly modified by filter)
+ * @refeed: whether we are refeeding protocol
+ */
 static inline void
 do_rte_announce(struct announce_hook *ah, int type UNUSED, net *net, rte *new, rte *old, ea_list *tmpa, int refeed)
 {
@@ -474,6 +484,15 @@ rte_recalculate(struct announce_hook *ah, net *net, rte *new, ea_list *tmpa, str
       return;
     }
 
+  struct proto_limit *l = ah->in_limit;
+  if (l && !old && new && (stats->imp_routes >= l->limit) && proto_notify_limit(ah, l))
+    {
+      stats->imp_updates_ignored++;
+      rte_trace_in(D_FILTERS, p, new, "ignored [limit]");
+      rte_free_quick(new);
+      return;
+    }
+
   if (new)
     stats->imp_updates_accepted++;
   else
index 4dd4b7be2054a8a671b011102504457112abbe88..cf743dff01ced3160f8706a702f1b4dedc324ded 100644 (file)
@@ -542,22 +542,6 @@ bgp_active(struct bgp_proto *p)
   bgp_start_timer(conn->connect_retry_timer, delay);
 }
 
-int
-bgp_apply_limits(struct bgp_proto *p)
-{
-  if (p->cf->route_limit && (p->p.stats.imp_routes > p->cf->route_limit))
-    {
-      log(L_WARN "%s: Route limit exceeded, shutting down", p->p.name);
-      bgp_store_error(p, NULL, BE_AUTO_DOWN, BEA_ROUTE_LIMIT_EXCEEDED);
-      bgp_update_startup_delay(p);
-      bgp_stop(p, 1); // Errcode 6, 1 - max number of prefixes reached
-      return -1;
-    }
-
-  return 0;
-}
-
-
 /**
  * bgp_connect - initiate an outgoing connection
  * @p: BGP instance
@@ -868,24 +852,46 @@ static int
 bgp_shutdown(struct proto *P)
 {
   struct bgp_proto *p = (struct bgp_proto *) P;
-  unsigned subcode;
+  unsigned subcode = 0;
 
   BGP_TRACE(D_EVENTS, "Shutdown requested");
-  bgp_store_error(p, NULL, BE_MAN_DOWN, 0);
 
-  if (P->reconfiguring)
+  switch (P->down_code)
     {
-      if (P->cf_new)
-       subcode = 6; // Errcode 6, 6 - other configuration change
+    case PDC_CF_REMOVE:
+    case PDC_CF_DISABLE:
+      subcode = 3; // Errcode 6, 3 - peer de-configured
+      break;
+
+    case PDC_CF_RESTART:
+      subcode = 6; // Errcode 6, 6 - other configuration change
+      break;
+
+    case PDC_CMD_DISABLE:
+      subcode = 2; // Errcode 6, 2 - administrative shutdown
+      break;
+
+    case PDC_CMD_RESTART:
+      subcode = 4; // Errcode 6, 4 - administrative reset
+      break;
+
+    case PDC_IN_LIMIT_HIT:
+      subcode = 1; // Errcode 6, 1 - max number of prefixes reached
+      log(L_WARN "%s: Route limit exceeded, shutting down", p->p.name);
+
+      bgp_store_error(p, NULL, BE_AUTO_DOWN, BEA_ROUTE_LIMIT_EXCEEDED);
+      if (P->cf->in_limit->action == PLA_RESTART)
+       bgp_update_startup_delay(p);
       else
-       subcode = 3; // Errcode 6, 3 - peer de-configured
+       p->startup_delay = 0;
+      goto done;
     }
-  else
-    subcode = 2; // Errcode 6, 2 - administrative shutdown
 
+  bgp_store_error(p, NULL, BE_MAN_DOWN, 0);
   p->startup_delay = 0;
-  bgp_stop(p, subcode);
 
+ done:
+  bgp_stop(p, subcode);
   return p->p.proto_state;
 }
 
@@ -969,6 +975,10 @@ bgp_check_config(struct bgp_config *c)
   /* Different default for gw_mode */
   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;
 }
 
 static int
@@ -1116,9 +1126,6 @@ bgp_get_status(struct proto *P, byte *buf)
     bsprintf(buf, "%-14s%s%s", bgp_state_dsc(p), err1, err2);
 }
 
-static inline bird_clock_t tm_remains(timer *t)
-{ return t->expires ? t->expires - now : 0; }
-
 static void
 bgp_show_proto_info(struct proto *P)
 {
index a8c5818e517a07b88bb1c54770b2ba810d4a25ef..aa2db4b093b51fa2ab813ad5e8e6adb90da80708 100644 (file)
@@ -152,7 +152,6 @@ void bgp_conn_enter_established_state(struct bgp_conn *conn);
 void bgp_conn_enter_close_state(struct bgp_conn *conn);
 void bgp_conn_enter_idle_state(struct bgp_conn *conn);
 void bgp_store_error(struct bgp_proto *p, struct bgp_conn *c, u8 class, u32 code);
-int bgp_apply_limits(struct bgp_proto *p);
 void bgp_stop(struct bgp_proto *p, unsigned subcode);
 
 
index 78ca52de6ad426f98566837b2f95512ac66f9b5c..5feaea0deb8167c5c443f3ac05630832365fd71c 100644 (file)
@@ -98,7 +98,11 @@ 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 IGP TABLE rtable ';' { BGP_CFG->igp_table = $4; }
index d3e9b6a1257f23a358653f93a8d54a18f1137824..168025d0aaa45191cedf6ca0df134cfb99b4cd3f 100644 (file)
@@ -915,9 +915,6 @@ bgp_do_rx_update(struct bgp_conn *conn,
          if (n = net_find(p->p.table, prefix, pxlen))
            rte_update(p->p.table, n, &p->p, &p->p, NULL);
        }
-
-      if (bgp_apply_limits(p) < 0)
-       goto done;
     }
 
  done:
index 40637558b4376b36ac0fc17fea37d1a789c0bbff..4fb2b499bb40f5643405a4488734197428cb27dc 100644 (file)
@@ -36,6 +36,7 @@ pipe_proto:
        cf_error("Routing table name expected");
      PIPE_CFG->peer = $4->def;
    }
+ | pipe_proto EXPORT LIMIT limit_spec ';' { PIPE_CFG->out_limit = $4; }
  | pipe_proto MODE OPAQUE ';' { PIPE_CFG->mode = PIPE_OPAQUE; }
  | pipe_proto MODE TRANSPARENT ';' { PIPE_CFG->mode = PIPE_TRANSPARENT; }
  ;
index 36b06d43b240de8f59ea4af999a267ece174200e..a5fcc6f65ecf2ac8c53f4b5e1094c9af730e44a5 100644 (file)
  * rte_update(), an import filter in ahook 2 is called. When a new
  * route is announced in the peer table, an export filter in ahook2
  * and an import filter in ahook 1 are used. Oviously, there is no
- * need in filtering the same route twice, so both import filters
- * are set to accept, while user configured 'import' and 'export'
- * filters are used as export filters in ahooks 2 and 1.
+ * need in filtering the same route twice, so both import filters are
+ * set to accept, while user configured 'import' and 'export' filters
+ * are used as export filters in ahooks 2 and 1. Route limits are
+ * handled similarly, but on the import side of ahooks.
  */
 
 #undef LOCAL_DEBUG
@@ -116,6 +117,8 @@ pipe_import_control(struct proto *P, rte **ee, ea_list **ea UNUSED, struct linpo
 static int
 pipe_reload_routes(struct proto *P)
 {
+  struct pipe_proto *p = (struct pipe_proto *) P;
+
   /*
    * Because the pipe protocol feeds routes from both routing tables
    * together, both directions are reloaded during refeed and 'reload
@@ -123,6 +126,12 @@ pipe_reload_routes(struct proto *P)
    * request refeed when 'reload in' command is used.
    */
   proto_request_feeding(P);
+
+  if (P->main_ahook->in_limit)
+    P->main_ahook->in_limit->active = 0;
+  if (p->peer_ahook->in_limit)
+    p->peer_ahook->in_limit->active = 0;
+
   return 1;
 }
 
@@ -146,6 +155,7 @@ pipe_init(struct proto_config *C)
 static int
 pipe_start(struct proto *P)
 {
+  struct pipe_config *cf = (struct pipe_config *) P->cf;
   struct pipe_proto *p = (struct pipe_proto *) P;
 
   /* Lock both tables, unlock is handled in pipe_cleanup() */
@@ -155,10 +165,13 @@ pipe_start(struct proto *P)
   /* Going directly to PS_UP - prepare for feeding,
      connect the protocol to both routing tables */
 
-  P->main_ahook = proto_add_announce_hook(P, P->table,
-    FILTER_ACCEPT, P->cf->out_filter, &P->stats);
-  p->peer_ahook = proto_add_announce_hook(P, p->peer_table,
-    FILTER_ACCEPT, P->cf->in_filter,  &p->peer_stats);
+  P->main_ahook = proto_add_announce_hook(P, P->table, &P->stats);
+  P->main_ahook->out_filter = cf->c.out_filter;
+  P->main_ahook->in_limit = cf->c.in_limit;
+
+  p->peer_ahook = proto_add_announce_hook(P, p->peer_table, &p->peer_stats);
+  p->peer_ahook->out_filter = cf->c.in_filter;
+  p->peer_ahook->in_limit = cf->out_limit;
 
   return PS_UP;
 }
@@ -204,10 +217,16 @@ pipe_reconfigure(struct proto *P, struct proto_config *new)
 
   /* Update output filters in ahooks */
   if (P->main_ahook)
-    P->main_ahook->out_filter = new->out_filter;
+    {
+      P->main_ahook->out_filter = new->out_filter;
+      P->main_ahook->in_limit = new->in_limit;
+    }
 
   if (p->peer_ahook)
-    p->peer_ahook->out_filter = new->in_filter;
+    {
+      p->peer_ahook->out_filter = new->in_filter;
+      p->peer_ahook->in_limit = nc->out_limit;
+    }
 
   if ((P->proto_state != PS_UP) || (proto_reconfig_type == RECONFIG_SOFT))
     return 1;
@@ -283,12 +302,16 @@ static void
 pipe_show_proto_info(struct proto *P)
 {
   struct pipe_proto *p = (struct pipe_proto *) P;
+  struct pipe_config *cf = (struct pipe_config *) P->cf;
 
   // cli_msg(-1006, "  Table:          %s", P->table->name);
   // cli_msg(-1006, "  Peer table:     %s", p->peer_table->name);
   cli_msg(-1006, "  Preference:     %d", P->preference);
-  cli_msg(-1006, "  Input filter:   %s", filter_name(P->cf->in_filter));
-  cli_msg(-1006, "  Output filter:  %s", filter_name(P->cf->out_filter));
+  cli_msg(-1006, "  Input filter:   %s", filter_name(cf->c.in_filter));
+  cli_msg(-1006, "  Output filter:  %s", filter_name(cf->c.out_filter));
+
+  proto_show_limit(cf->c.in_limit, "Import limit:");
+  proto_show_limit(cf->out_limit, "Export limit:");
 
   if (P->proto_state != PS_DOWN)
     pipe_show_stats(p);
index 50b3169815f4c93a5bab944b2eb89ecd55887a58..e777fb417a481219df4824195bdcc136a4d00363 100644 (file)
@@ -15,6 +15,7 @@
 struct pipe_config {
   struct proto_config c;
   struct rtable_config *peer;          /* Table we're connected to */
+  struct proto_limit *out_limit;       /* Export route limit */
   int mode;                            /* PIPE_OPAQUE or PIPE_TRANSPARENT */
 };
 
index a20df483c73eb60ece7fe1b30a704be8172f699a..a788ae270791454a7952e2b8f4c7075e3b0ec510 100644 (file)
@@ -30,6 +30,22 @@ void tm_start(timer *, unsigned after);
 void tm_stop(timer *);
 void tm_dump_all(void);
 
+extern bird_clock_t now;               /* Relative, monotonic time in seconds */
+extern bird_clock_t now_real;          /* Time in seconds since fixed known epoch */
+
+static inline bird_clock_t
+tm_remains(timer *t)
+{
+  return t->expires ? t->expires - now : 0;
+}
+
+static inline void
+tm_start_max(timer *t, unsigned after)
+{
+  bird_clock_t rem = tm_remains(t);
+  tm_start(t, (rem > after) ? rem : after);
+}
+
 static inline timer *
 tm_new_set(pool *p, void (*hook)(struct timer *), void *data, unsigned rand, unsigned rec)
 {
@@ -41,8 +57,6 @@ tm_new_set(pool *p, void (*hook)(struct timer *), void *data, unsigned rand, uns
   return t;
 }
 
-extern bird_clock_t now;               /* Relative, monotonic time in seconds */
-extern bird_clock_t now_real;          /* Time in seconds since fixed known epoch */
 
 struct timeformat {
   char *fmt1, *fmt2;