Revision cf98be7b

View differences:

doc/bird.sgml
459 459
	works in the direction from the routing table to the protocol.
460 460
	Default: <cf/none/.
461 461

  
462
	<tag>import keep rejected <m/bool/</tag>
463
	Usually, if an import filter rejects a route, the route is
464
	forgotten. When this option is active, rejected routes are
465
	kept in the routing table, but they are hidden and not
466
	propagated to other protocols. But it is possible to show them
467
	using <cf/show route rejected/. Note that this option does not
468
	work for the pipe protocol. Default: off.
469

  
462 470
	<tag>import limit <m/number/ [action warn | block | restart | disable]</tag>
463 471
	Specify an import route limit (a maximum number of routes
464 472
	imported from the protocol) and optionally the action to be
......
467 475
	protocol. Restart and disable actions shut the protocol down
468 476
	like appropriate commands. Disable is the default action if an
469 477
	action is not explicitly specified. Note that limits are reset
470
	during protocol reconfigure, reload or restart.
471
	Default: <cf/none/.
478
	during protocol reconfigure, reload or restart. Also note that
479
	if <cf/import keep rejected/ is active, rejected routes are
480
	counted towards the limit and blocked routes are forgotten, as
481
	the main purpose of the import limit is to protect routing
482
	tables from overflow. Default: <cf/none/.
472 483

  
473 484
	<tag>export limit <m/number/ [action warn | block | restart | disable]</tag>
474 485
	Specify an export route limit, works similarly to
......
661 672
	<p>You can also select just routes added by a specific protocol.
662 673
	<cf>protocol <m/p/</cf>.
663 674

  
675
	<p>If BIRD is configured to keep rejected routes (see </cf/import keep rejected/
676
	option), you can show them instead of routes by using </cf/rejected/ switch.
677

  
664 678
	<p>The <cf/stats/ switch requests showing of route statistics (the
665 679
	number of networks, number of routes before and after filtering). If
666 680
	you use <cf/count/ instead, only the statistics will be printed.
......
2460 2474
	router. 0 means do not use as a default router. Default: 3 *
2461 2475
	<cf/max ra interval/.
2462 2476

  
2463
	<tag>rdnss local <m/bool/</tag>
2477
	<tag>rdnss local <m/switch/</tag>
2464 2478
	Use only local (interface-specific) RDNSS definitions for this
2465 2479
	interface. Otherwise, both global and local definitions are
2466 2480
	used. Could also be used to disable RDNSS for given interface
2467 2481
	if no local definitons are specified. Default: no.
2468 2482

  
2469
	<tag>dnssl local <m/bool/</tag>
2483
	<tag>dnssl local <m/switch/</tag>
2470 2484
	Use only local DNSSL definitions for this interface. See
2471 2485
	<cf/rdnss local/ option above. Default: no.
2472 2486
</descrip>
nest/config.Y
44 44

  
45 45
CF_KEYWORDS(ROUTER, ID, PROTOCOL, TEMPLATE, PREFERENCE, DISABLED, DEBUG, ALL, OFF, DIRECT)
46 46
CF_KEYWORDS(INTERFACE, IMPORT, EXPORT, FILTER, NONE, TABLE, STATES, ROUTES, FILTERS)
47
CF_KEYWORDS(LIMIT, ACTION, WARN, BLOCK, RESTART, DISABLE)
47
CF_KEYWORDS(LIMIT, ACTION, WARN, BLOCK, RESTART, DISABLE, KEEP, REJECTED)
48 48
CF_KEYWORDS(PASSWORD, FROM, PASSIVE, TO, ID, EVENTS, PACKETS, PROTOCOLS, INTERFACES)
49 49
CF_KEYWORDS(PRIMARY, STATS, COUNT, FOR, COMMANDS, PREEXPORT, GENERATE, ROA, MAX, FLUSH)
50 50
CF_KEYWORDS(LISTEN, BGP, V6ONLY, DUAL, ADDRESS, PORT, PASSWORDS, DESCRIPTION, SORTED)
......
187 187
 | EXPORT imexport { this_proto->out_filter = $2; }
188 188
 | IMPORT LIMIT limit_spec { this_proto->in_limit = $3; }
189 189
 | EXPORT LIMIT limit_spec { this_proto->out_limit = $3; }
190
 | IMPORT KEEP REJECTED bool { this_proto->in_keep_rejected = $4; }
190 191
 | TABLE rtable { this_proto->table = $2; }
191 192
 | ROUTER ID idval { this_proto->router_id = $3; }
192 193
 | DESCRIPTION TEXT { this_proto->dsc = $2; }
......
405 406
{ if_show_summary(); } ;
406 407

  
407 408
CF_CLI_HELP(SHOW ROUTE, ..., [[Show routing table]])
408
CF_CLI(SHOW ROUTE, r_args, [[[<prefix>|for <prefix>|for <ip>] [table <t>] [filter <f>|where <cond>] [all] [primary] [(export|preexport) <p>] [protocol <p>] [stats|count]]], [[Show routing table]])
409
CF_CLI(SHOW ROUTE, r_args, [[[<prefix>|for <prefix>|for <ip>] [table <t>] [filter <f>|where <cond>] [all] [primary] [rejected] [(export|preexport) <p>] [protocol <p>] [stats|count]]], [[Show routing table]])
409 410
{ rt_show($3); } ;
410 411

  
411 412
r_args:
......
451 452
     $$ = $1;
452 453
     $$->primary_only = 1;
453 454
   }
455
 | r_args REJECTED {
456
     $$ = $1;
457
     $$->rejected = 1;
458
   }
454 459
 | r_args export_or_preexport SYM {
455 460
     struct proto_config *c = (struct proto_config *) $3->def;
456 461
     $$ = $1;
nest/proto.c
414 414
      p->main_ahook->out_filter = nc->out_filter;
415 415
      p->main_ahook->in_limit = nc->in_limit;
416 416
      p->main_ahook->out_limit = nc->out_limit;
417
      p->main_ahook->in_keep_rejected = nc->in_keep_rejected;
417 418
    }
418 419

  
419 420
  /* Update routes when filters changed. If the protocol in not UP,
......
719 720
{
720 721
  DBG("Protocol %s down\n", p->name);
721 722

  
722
  if (p->stats.imp_routes != 0)
723
    log(L_ERR "Protocol %s is down but still has %d routes", p->name, p->stats.imp_routes);
723
  u32 all_routes = p->stats.imp_routes + p->stats.rej_routes;
724
  if (all_routes != 0)
725
    log(L_ERR "Protocol %s is down but still has %d routes", p->name, all_routes);
724 726

  
725 727
  bzero(&p->stats, sizeof(struct proto_stats));
726 728
  proto_free_ahooks(p);
......
796 798
      p->main_ahook->out_filter = p->cf->out_filter;
797 799
      p->main_ahook->in_limit = p->cf->in_limit;
798 800
      p->main_ahook->out_limit = p->cf->out_limit;
801
      p->main_ahook->in_keep_rejected = p->cf->in_keep_rejected;
799 802
      proto_reset_limit(p->main_ahook->in_limit);
800 803
      proto_reset_limit(p->main_ahook->out_limit);
801 804
    }
......
1093 1096
}
1094 1097

  
1095 1098
static void
1096
proto_show_stats(struct proto_stats *s)
1099
proto_show_stats(struct proto_stats *s, int in_keep_rejected)
1097 1100
{
1098
  cli_msg(-1006, "  Routes:         %u imported, %u exported, %u preferred", 
1099
	  s->imp_routes, s->exp_routes, s->pref_routes);
1101
  if (in_keep_rejected)
1102
    cli_msg(-1006, "  Routes:         %u imported, %u rejected, %u exported, %u preferred", 
1103
	    s->imp_routes, s->rej_routes, s->exp_routes, s->pref_routes);
1104
  else
1105
    cli_msg(-1006, "  Routes:         %u imported, %u exported, %u preferred", 
1106
	    s->imp_routes, s->exp_routes, s->pref_routes);
1107

  
1100 1108
  cli_msg(-1006, "  Route change stats:     received   rejected   filtered    ignored   accepted");
1101 1109
  cli_msg(-1006, "    Import updates:     %10u %10u %10u %10u %10u",
1102 1110
	  s->imp_updates_received, s->imp_updates_invalid,
......
1134 1142
  proto_show_limit(p->cf->out_limit, "Export limit:");
1135 1143

  
1136 1144
  if (p->proto_state != PS_DOWN)
1137
    proto_show_stats(&p->stats);
1145
    proto_show_stats(&p->stats, p->cf->in_keep_rejected);
1138 1146
}
1139 1147

  
1140 1148
void
nest/protocol.h
91 91
  int class;				/* SYM_PROTO or SYM_TEMPLATE */
92 92
  u32 debug, mrtdump;			/* Debugging bitfields, both use D_* constants */
93 93
  unsigned preference, disabled;	/* Generic parameters */
94
  int in_keep_rejected;			/* Routes rejected in import filter are kept */
94 95
  u32 router_id;			/* Protocol specific router ID */
95 96
  struct rtable_config *table;		/* Table we're attached to */
96 97
  struct filter *in_filter, *out_filter; /* Attached filters */
......
106 107
struct proto_stats {
107 108
  /* Import - from protocol to core */
108 109
  u32 imp_routes;		/* Number of routes successfully imported to the (adjacent) routing table */
109
  u32 pref_routes;		/* Number of routes that are preferred, sum over all routing table */
110
  u32 rej_routes;		/* Number of routes rejected in import filter but kept in the routing table */
111
  u32 pref_routes;		/* Number of routes that are preferred, sum over all routing tables */
110 112
  u32 imp_updates_received;	/* Number of route updates received */
111 113
  u32 imp_updates_invalid;	/* Number of route updates rejected as invalid */
112 114
  u32 imp_updates_filtered;	/* Number of route updates rejected by filters */
......
410 412
  struct proto_limit *out_limit;	/* Output limit */
411 413
  struct proto_stats *stats;		/* Per-table protocol statistics */
412 414
  struct announce_hook *next;		/* Next hook for the same protocol */
415
  int in_keep_rejected;			/* Routes rejected in import filter are kept */
413 416
};
414 417

  
415 418
struct announce_hook *proto_add_announce_hook(struct proto *p, struct rtable *t, struct proto_stats *stats);
nest/route.h
221 221
} rte;
222 222

  
223 223
#define REF_COW		1		/* Copy this rte on write */
224
#define REF_REJECTED	2		/* Route is rejected by import filter */
225

  
226
/* Route is valid for propagation (may depend on other flags in the future), accepts NULL */
227
static inline int rte_is_valid(rte *r) { return r && !(r->flags & REF_REJECTED); }
228

  
229
/* Route just has REF_REJECTED flag */
230
static inline int rte_is_rejected(rte *r) { return !!(r->flags & REF_REJECTED); }
231

  
224 232

  
225 233
/* Types of route announcement, also used as flags */
226 234
#define RA_OPTIMAL	1		/* Announcement of optimal route change */
......
263 271
  struct fib_iterator fit;
264 272
  struct proto *show_protocol;
265 273
  struct proto *export_protocol;
266
  int export_mode, primary_only;
274
  int export_mode, primary_only, rejected;
267 275
  struct config *running_on_config;
268 276
  int net_counter, rt_counter, show_counter;
269 277
  int stats, show_for;
nest/rt-table.c
69 69
    {
70 70
      a0 = ipa_and(a, ipa_mkmask(len));
71 71
      n = fib_find(&tab->fib, &a0, len);
72
      if (n && n->routes)
72
      if (n && rte_is_valid(n->routes))
73 73
	return n;
74 74
      len--;
75 75
    }
......
139 139
{
140 140
  int (*better)(rte *, rte *);
141 141

  
142
  if (!old)
142
  if (!rte_is_valid(old))
143 143
    return 1;
144
  if (!rte_is_valid(new))
145
    return 0;
146

  
144 147
  if (new->pref > old->pref)
145 148
    return 1;
146 149
  if (new->pref < old->pref)
......
399 402
  rte *old_free = NULL;
400 403
  rte *r;
401 404

  
402
  /* Used to track whether we met old_changed position. If it is NULL
403
     it was the first and met it implicitly before current best route. */
404
  int old_meet = (old_changed && !before_old) ? 1 : 0;
405
  /* Used to track whether we met old_changed position. If before_old is NULL
406
     old_changed was the first and we met it implicitly before current best route. */
407
  int old_meet = old_changed && !before_old;
408

  
409
  /* Note that before_old is either NULL or valid (not rejected) route.
410
     If old_changed is valid, before_old have to be too. If old changed route
411
     was not valid, caller must use NULL for both old_changed and before_old. */
405 412

  
406 413
  if (new_changed)
407 414
    stats->exp_updates_received++;
......
409 416
    stats->exp_withdraws_received++;
410 417

  
411 418
  /* First, find the new_best route - first accepted by filters */
412
  for (r=net->routes; r; r=r->next)
419
  for (r=net->routes; rte_is_valid(r); r=r->next)
413 420
    {
414 421
      if (new_best = export_filter(ah, r, &new_free, &tmpa, 0))
415 422
	break;
......
428 435
  if (feed)
429 436
    {
430 437
      if (feed == 2)	/* refeed */
431
	old_best = new_best ? new_best : net->routes;
438
	old_best = new_best ? new_best :
439
	  (rte_is_valid(net->routes) ? net->routes : NULL);
432 440
      else
433 441
	old_best = NULL;
434 442

  
......
477 485
    }
478 486

  
479 487
  /* Fourth case */
480
  for (r=r->next; r; r=r->next)
488
  for (r=r->next; rte_is_valid(r); r=r->next)
481 489
    {
482 490
      if (old_best = export_filter(ah, r, &old_free, NULL, 1))
483 491
	goto found;
......
531 539
static void
532 540
rte_announce(rtable *tab, unsigned type, net *net, rte *new, rte *old, rte *before_old, ea_list *tmpa)
533 541
{
534
  struct announce_hook *a;
542
  if (!rte_is_valid(old))
543
    old = before_old = NULL;
544

  
545
  if (!rte_is_valid(new))
546
    new = NULL;
547

  
548
  if (!old && !new)
549
    return;
535 550

  
536 551
  if (type == RA_OPTIMAL)
537 552
    {
......
544 559
	rt_notify_hostcache(tab, net);
545 560
    }
546 561

  
562
  struct announce_hook *a;
547 563
  WALK_LIST(a, tab->hooks)
548 564
    {
549 565
      ASSERT(a->proto->core_state == FS_HAPPY || a->proto->core_state == FS_FEEDING);
......
650 666
	  if (new && rte_same(old, new))
651 667
	    {
652 668
	      /* No changes, ignore the new route */
653
	      stats->imp_updates_ignored++;
654
	      rte_trace_in(D_ROUTES, p, new, "ignored");
669

  
670
	      if (!rte_is_rejected(new))
671
		{
672
		  stats->imp_updates_ignored++;
673
		  rte_trace_in(D_ROUTES, p, new, "ignored");
674
		}
675

  
655 676
	      rte_free_quick(new);
656 677
#ifdef CONFIG_RIP
657 678
	      /* lastmod is used internally by RIP as the last time
......
680 701
  struct proto_limit *l = ah->in_limit;
681 702
  if (l && !old && new)
682 703
    {
683
      if (stats->imp_routes >= l->limit)
684
	proto_notify_limit(ah, l, stats->imp_routes);
704
      u32 all_routes = stats->imp_routes + stats->rej_routes;
705

  
706
      if (all_routes >= l->limit)
707
	proto_notify_limit(ah, l, all_routes);
685 708

  
686 709
      if (l->state == PLS_BLOCKED)
687 710
	{
......
692 715
	}
693 716
    }
694 717

  
695
  if (new)
718
  if (new && !rte_is_rejected(new))
696 719
    stats->imp_updates_accepted++;
697 720
  else
698 721
    stats->imp_withdraws_accepted++;
699 722

  
700 723
  if (new)
701
    stats->imp_routes++;
724
    rte_is_rejected(new) ? stats->rej_routes++ : stats->imp_routes++;
702 725
  if (old)
703
    stats->imp_routes--;
726
    rte_is_rejected(old) ? stats->rej_routes-- : stats->imp_routes--;
704 727

  
705 728
  if (table->config->sorted)
706 729
    {
......
900 923
	  stats->imp_updates_invalid++;
901 924
	  goto drop;
902 925
	}
926

  
903 927
      if (filter == FILTER_REJECT)
904 928
	{
905 929
	  stats->imp_updates_filtered++;
906 930
	  rte_trace_in(D_FILTERS, p, new, "filtered out");
907
	  goto drop;
931

  
932
	  if (! ah->in_keep_rejected)
933
	    goto drop;
934

  
935
	  /* new is a private copy, i could modify it */
936
	  new->flags |= REF_REJECTED;
908 937
	}
909
      if (src->make_tmp_attrs)
910
	tmpa = src->make_tmp_attrs(new, rte_update_pool);
911
      if (filter)
938
      else
912 939
	{
913
	  ea_list *old_tmpa = tmpa;
914
	  int fr = f_run(filter, &new, &tmpa, rte_update_pool, 0);
915
	  if (fr > F_ACCEPT)
940
	  if (src->make_tmp_attrs)
941
	    tmpa = src->make_tmp_attrs(new, rte_update_pool);
942
	  if (filter && (filter != FILTER_REJECT))
916 943
	    {
917
	      stats->imp_updates_filtered++;
918
	      rte_trace_in(D_FILTERS, p, new, "filtered out");
919
	      goto drop;
944
	      ea_list *old_tmpa = tmpa;
945
	      int fr = f_run(filter, &new, &tmpa, rte_update_pool, 0);
946
	      if (fr > F_ACCEPT)
947
		{
948
		  stats->imp_updates_filtered++;
949
		  rte_trace_in(D_FILTERS, p, new, "filtered out");
950

  
951
		  if (! ah->in_keep_rejected)
952
		    goto drop;
953

  
954
		  new->flags |= REF_REJECTED;
955
		}
956
	      if (tmpa != old_tmpa && src->store_tmp_attrs)
957
		src->store_tmp_attrs(new, tmpa);
920 958
	    }
921
	  if (tmpa != old_tmpa && src->store_tmp_attrs)
922
	    src->store_tmp_attrs(new, tmpa);
923 959
	}
960

  
924 961
      if (!(new->attrs->aflags & RTAF_CACHED)) /* Need to copy attributes */
925 962
	new->attrs = rta_lookup(new->attrs);
926 963
      new->flags |= REF_COW;
......
1553 1590
	  return 0;
1554 1591
	}
1555 1592

  
1593
      /* XXXX perhaps we should change feed for RA_ACCEPTED to not use 'new' */
1594

  
1556 1595
      if ((p->accept_ra_types == RA_OPTIMAL) ||
1557 1596
	  (p->accept_ra_types == RA_ACCEPTED))
1558
	if (e)
1597
	if (rte_is_valid(e))
1559 1598
	  {
1560 1599
	    if (p->core_state != FS_FEEDING)
1561 1600
	      return 1;  /* In the meantime, the protocol fell down. */
......
1564 1603
	  }
1565 1604

  
1566 1605
      if (p->accept_ra_types == RA_ANY)
1567
	for(e = n->routes; e != NULL; e = e->next)
1606
	for(e = n->routes; rte_is_valid(e); e = e->next)
1568 1607
	  {
1569 1608
	    if (p->core_state != FS_FEEDING)
1570 1609
	      return 1;  /* In the meantime, the protocol fell down. */
......
1817 1856
  net *n = net_route(tab, he->addr, MAX_PREFIX_LENGTH);
1818 1857
  if (n)
1819 1858
    {
1820
      rta *a = n->routes->attrs;
1859
      rte *e = n->routes;
1860
      rta *a = e->attrs;
1821 1861
      pxlen = n->n.pxlen;
1822 1862

  
1823 1863
      if (a->hostentry)
......
1850 1890
	}
1851 1891

  
1852 1892
      he->src = rta_clone(a);
1853
      he->igp_metric = rt_get_igp_metric(n->routes);
1893
      he->igp_metric = rt_get_igp_metric(e);
1854 1894
    }
1855 1895

  
1856 1896
 done:
......
1980 2020
  int ok;
1981 2021

  
1982 2022
  bsprintf(ia, "%I/%d", n->n.prefix, n->n.pxlen);
1983
  if (n->routes)
1984
    d->net_counter++;
2023

  
1985 2024
  for(e=n->routes; e; e=e->next)
1986 2025
    {
2026
      if (rte_is_rejected(e) != d->rejected)
2027
	continue;
2028

  
1987 2029
      struct ea_list *tmpa;
1988 2030
      struct proto *p0 = e->attrs->proto;
1989 2031
      struct proto *p1 = d->export_protocol;
1990 2032
      struct proto *p2 = d->show_protocol;
2033

  
2034
      if (ia[0])
2035
	d->net_counter++;
1991 2036
      d->rt_counter++;
1992 2037
      ee = e;
1993 2038
      rte_update_lock();		/* We use the update buffer for filtering */
proto/bgp/attrs.c
1346 1346

  
1347 1347
  /* The default case - find a new best-in-group route */
1348 1348
  r = new; /* new may not be in the list */
1349
  for (s=net->routes; s; s=s->next)
1349
  for (s=net->routes; rte_is_valid(s); s=s->next)
1350 1350
    if (use_deterministic_med(s) && same_group(s, lpref, lasn))
1351 1351
      {
1352 1352
	s->u.bgp.suppressed = 1;
proto/bgp/bgp.c
1188 1188
      cli_msg(-1006, "    Source address:   %I", p->source_addr);
1189 1189
      if (P->cf->in_limit)
1190 1190
	cli_msg(-1006, "    Route limit:      %d/%d",
1191
		p->p.stats.imp_routes, P->cf->in_limit->limit);
1191
		p->p.stats.imp_routes + p->p.stats.rej_routes, P->cf->in_limit->limit);
1192 1192
      cli_msg(-1006, "    Hold timer:       %d/%d",
1193 1193
	      tm_remains(c->hold_timer), c->hold_time);
1194 1194
      cli_msg(-1006, "    Keepalive timer:  %d/%d",
sysdep/unix/krt.c
581 581
    {
582 582
      net *n = (net *) f;
583 583
      rte *e = n->routes;
584
      if (e && (n->n.flags & KRF_INSTALLED))
584
      if (rte_is_valid(e) && (n->n.flags & KRF_INSTALLED))
585 585
	{
586 586
	  /* FIXME: this does not work if gw is changed in export filter */
587 587
	  krt_replace_rte(p, e->net, NULL, e, NULL);
......
656 656
    }
657 657

  
658 658
  old = net->routes;
659
  if ((net->n.flags & KRF_INSTALLED) && old)
659
  if ((net->n.flags & KRF_INSTALLED) && rte_is_valid(old))
660 660
    {
661 661
      /* There may be changes in route attributes, we ignore that.
662 662
         Also, this does not work well if gw is changed in export filter */

Also available in: Unified diff