File:  [ELWIX - Embedded LightWeight unIX -] / embedaddon / bird2 / proto / pipe / pipe.c
Revision 1.1.1.1 (vendor branch): download - view: text, annotated - select for diffs - revision graph
Mon Oct 21 16:03:57 2019 UTC (5 years, 5 months ago) by misho
Branches: bird2, MAIN
CVS tags: v2_0_7p0, HEAD
bird2 ver 2.0.7

    1: /*
    2:  *	BIRD -- Table-to-Table Routing Protocol a.k.a Pipe
    3:  *
    4:  *	(c) 1999--2000 Martin Mares <mj@ucw.cz>
    5:  *
    6:  *	Can be freely distributed and used under the terms of the GNU GPL.
    7:  */
    8: 
    9: /**
   10:  * DOC: Pipe
   11:  *
   12:  * The Pipe protocol is very simple. It just connects to two routing tables
   13:  * using proto_add_announce_hook() and whenever it receives a rt_notify()
   14:  * about a change in one of the tables, it converts it to a rte_update()
   15:  * in the other one.
   16:  *
   17:  * To avoid pipe loops, Pipe keeps a `being updated' flag in each routing
   18:  * table.
   19:  *
   20:  * A pipe has two announce hooks, the first connected to the main
   21:  * table, the second connected to the peer table. When a new route is
   22:  * announced on the main table, it gets checked by an export filter in
   23:  * ahook 1, and, after that, it is announced to the peer table via
   24:  * rte_update(), an import filter in ahook 2 is called. When a new
   25:  * route is announced in the peer table, an export filter in ahook2
   26:  * and an import filter in ahook 1 are used. Oviously, there is no
   27:  * need in filtering the same route twice, so both import filters are
   28:  * set to accept, while user configured 'import' and 'export' filters
   29:  * are used as export filters in ahooks 2 and 1. Route limits are
   30:  * handled similarly, but on the import side of ahooks.
   31:  */
   32: 
   33: #undef LOCAL_DEBUG
   34: 
   35: #include "nest/bird.h"
   36: #include "nest/iface.h"
   37: #include "nest/protocol.h"
   38: #include "nest/route.h"
   39: #include "nest/cli.h"
   40: #include "conf/conf.h"
   41: #include "filter/filter.h"
   42: #include "lib/string.h"
   43: 
   44: #include "pipe.h"
   45: 
   46: static void
   47: pipe_rt_notify(struct proto *P, struct channel *src_ch, net *n, rte *new, rte *old)
   48: {
   49:   struct pipe_proto *p = (void *) P;
   50:   struct channel *dst = (src_ch == p->pri) ? p->sec : p->pri;
   51:   struct rte_src *src;
   52: 
   53:   rte *e;
   54:   rta *a;
   55: 
   56:   if (!new && !old)
   57:     return;
   58: 
   59:   if (dst->table->pipe_busy)
   60:     {
   61:       log(L_ERR "Pipe loop detected when sending %N to table %s",
   62: 	  n->n.addr, dst->table->name);
   63:       return;
   64:     }
   65: 
   66:   if (new)
   67:     {
   68:       a = alloca(rta_size(new->attrs));
   69:       memcpy(a, new->attrs, rta_size(new->attrs));
   70: 
   71:       a->aflags = 0;
   72:       a->hostentry = NULL;
   73:       e = rte_get_temp(a);
   74:       e->pflags = 0;
   75: 
   76:       /* Copy protocol specific embedded attributes. */
   77:       memcpy(&(e->u), &(new->u), sizeof(e->u));
   78:       e->pref = new->pref;
   79:       e->pflags = new->pflags;
   80: 
   81: #ifdef CONFIG_BGP
   82:       /* Hack to cleanup cached value */
   83:       if (e->attrs->src->proto->proto == &proto_bgp)
   84: 	e->u.bgp.stale = -1;
   85: #endif
   86: 
   87:       src = a->src;
   88:     }
   89:   else
   90:     {
   91:       e = NULL;
   92:       src = old->attrs->src;
   93:     }
   94: 
   95:   src_ch->table->pipe_busy = 1;
   96:   rte_update2(dst, n->n.addr, e, src);
   97:   src_ch->table->pipe_busy = 0;
   98: }
   99: 
  100: static int
  101: pipe_preexport(struct proto *P, rte **ee, struct linpool *p UNUSED)
  102: {
  103:   struct proto *pp = (*ee)->sender->proto;
  104: 
  105:   if (pp == P)
  106:     return -1;	/* Avoid local loops automatically */
  107: 
  108:   return 0;
  109: }
  110: 
  111: static void
  112: pipe_reload_routes(struct channel *C)
  113: {
  114:   struct pipe_proto *p = (void *) C->proto;
  115: 
  116:   /* Route reload on one channel is just refeed on the other */
  117:   channel_request_feeding((C == p->pri) ? p->sec : p->pri);
  118: }
  119: 
  120: 
  121: static void
  122: pipe_postconfig(struct proto_config *CF)
  123: {
  124:   struct pipe_config *cf = (void *) CF;
  125:   struct channel_config *cc = proto_cf_main_channel(CF);
  126: 
  127:   if (!cc->table)
  128:     cf_error("Primary routing table not specified");
  129: 
  130:   if (!cf->peer)
  131:     cf_error("Secondary routing table not specified");
  132: 
  133:   if (cc->table == cf->peer)
  134:     cf_error("Primary table and peer table must be different");
  135: 
  136:   if (cc->table->addr_type != cf->peer->addr_type)
  137:     cf_error("Primary table and peer table must have the same type");
  138: 
  139:   if (cc->rx_limit.action)
  140:     cf_error("Pipe protocol does not support receive limits");
  141: 
  142:   if (cc->in_keep_filtered)
  143:     cf_error("Pipe protocol prohibits keeping filtered routes");
  144: }
  145: 
  146: static int
  147: pipe_configure_channels(struct pipe_proto *p, struct pipe_config *cf)
  148: {
  149:   struct channel_config *cc = proto_cf_main_channel(&cf->c);
  150: 
  151:   struct channel_config pri_cf = {
  152:     .name = "pri",
  153:     .channel = cc->channel,
  154:     .table = cc->table,
  155:     .out_filter = cc->out_filter,
  156:     .in_limit = cc->in_limit,
  157:     .ra_mode = RA_ANY
  158:   };
  159: 
  160:   struct channel_config sec_cf = {
  161:     .name = "sec",
  162:     .channel = cc->channel,
  163:     .table = cf->peer,
  164:     .out_filter = cc->in_filter,
  165:     .in_limit = cc->out_limit,
  166:     .ra_mode = RA_ANY
  167:   };
  168: 
  169:   return
  170:     proto_configure_channel(&p->p, &p->pri, &pri_cf) &&
  171:     proto_configure_channel(&p->p, &p->sec, &sec_cf);
  172: }
  173: 
  174: static struct proto *
  175: pipe_init(struct proto_config *CF)
  176: {
  177:   struct proto *P = proto_new(CF);
  178:   struct pipe_proto *p = (void *) P;
  179:   struct pipe_config *cf = (void *) CF;
  180: 
  181:   P->rt_notify = pipe_rt_notify;
  182:   P->preexport = pipe_preexport;
  183:   P->reload_routes = pipe_reload_routes;
  184: 
  185:   pipe_configure_channels(p, cf);
  186: 
  187:   return P;
  188: }
  189: 
  190: static int
  191: pipe_reconfigure(struct proto *P, struct proto_config *CF)
  192: {
  193:   struct pipe_proto *p = (void *) P;
  194:   struct pipe_config *cf = (void *) CF;
  195: 
  196:   return pipe_configure_channels(p, cf);
  197: }
  198: 
  199: static void
  200: pipe_copy_config(struct proto_config *dest UNUSED, struct proto_config *src UNUSED)
  201: {
  202:   /* Just a shallow copy, not many items here */
  203: }
  204: 
  205: static void
  206: pipe_get_status(struct proto *P, byte *buf)
  207: {
  208:   struct pipe_proto *p = (void *) P;
  209: 
  210:   bsprintf(buf, "%s <=> %s", p->pri->table->name, p->sec->table->name);
  211: }
  212: 
  213: static void
  214: pipe_show_stats(struct pipe_proto *p)
  215: {
  216:   struct proto_stats *s1 = &p->pri->stats;
  217:   struct proto_stats *s2 = &p->sec->stats;
  218: 
  219:   /*
  220:    * Pipe stats (as anything related to pipes) are a bit tricky. There
  221:    * are two sets of stats - s1 for ahook to the primary routing and
  222:    * s2 for the ahook to the secondary routing table. The user point
  223:    * of view is that routes going from the primary routing table to
  224:    * the secondary routing table are 'exported', while routes going in
  225:    * the other direction are 'imported'.
  226:    *
  227:    * Each route going through a pipe is, technically, first exported
  228:    * to the pipe and then imported from that pipe and such operations
  229:    * are counted in one set of stats according to the direction of the
  230:    * route propagation. Filtering is done just in the first part
  231:    * (export). Therefore, we compose stats for one directon for one
  232:    * user direction from both import and export stats, skipping
  233:    * immediate and irrelevant steps (exp_updates_accepted,
  234:    * imp_updates_received, imp_updates_filtered, ...).
  235:    *
  236:    * Rule of thumb is that stats s1 have the correct 'polarity'
  237:    * (imp/exp), while stats s2 have switched 'polarity'.
  238:    */
  239: 
  240:   cli_msg(-1006, "  Routes:         %u imported, %u exported",
  241: 	  s1->imp_routes, s2->imp_routes);
  242:   cli_msg(-1006, "  Route change stats:     received   rejected   filtered    ignored   accepted");
  243:   cli_msg(-1006, "    Import updates:     %10u %10u %10u %10u %10u",
  244: 	  s2->exp_updates_received, s2->exp_updates_rejected + s1->imp_updates_invalid,
  245: 	  s2->exp_updates_filtered, s1->imp_updates_ignored, s1->imp_updates_accepted);
  246:   cli_msg(-1006, "    Import withdraws:   %10u %10u        --- %10u %10u",
  247: 	  s2->exp_withdraws_received, s1->imp_withdraws_invalid,
  248: 	  s1->imp_withdraws_ignored, s1->imp_withdraws_accepted);
  249:   cli_msg(-1006, "    Export updates:     %10u %10u %10u %10u %10u",
  250: 	  s1->exp_updates_received, s1->exp_updates_rejected + s2->imp_updates_invalid,
  251: 	  s1->exp_updates_filtered, s2->imp_updates_ignored, s2->imp_updates_accepted);
  252:   cli_msg(-1006, "    Export withdraws:   %10u %10u        --- %10u %10u",
  253: 	  s1->exp_withdraws_received, s2->imp_withdraws_invalid,
  254: 	  s2->imp_withdraws_ignored, s2->imp_withdraws_accepted);
  255: }
  256: 
  257: static const char *pipe_feed_state[] = { [ES_DOWN] = "down", [ES_FEEDING] = "feed", [ES_READY] = "up" };
  258: 
  259: static void
  260: pipe_show_proto_info(struct proto *P)
  261: {
  262:   struct pipe_proto *p = (void *) P;
  263: 
  264:   cli_msg(-1006, "  Channel %s", "main");
  265:   cli_msg(-1006, "    Table:          %s", p->pri->table->name);
  266:   cli_msg(-1006, "    Peer table:     %s", p->sec->table->name);
  267:   cli_msg(-1006, "    Import state:   %s", pipe_feed_state[p->sec->export_state]);
  268:   cli_msg(-1006, "    Export state:   %s", pipe_feed_state[p->pri->export_state]);
  269:   cli_msg(-1006, "    Import filter:  %s", filter_name(p->sec->out_filter));
  270:   cli_msg(-1006, "    Export filter:  %s", filter_name(p->pri->out_filter));
  271: 
  272:   channel_show_limit(&p->pri->in_limit, "Import limit:");
  273:   channel_show_limit(&p->sec->in_limit, "Export limit:");
  274: 
  275:   if (P->proto_state != PS_DOWN)
  276:     pipe_show_stats(p);
  277: }
  278: 
  279: 
  280: struct protocol proto_pipe = {
  281:   .name =		"Pipe",
  282:   .template =		"pipe%d",
  283:   .class =		PROTOCOL_PIPE,
  284:   .proto_size =		sizeof(struct pipe_proto),
  285:   .config_size =	sizeof(struct pipe_config),
  286:   .postconfig =		pipe_postconfig,
  287:   .init =		pipe_init,
  288:   .reconfigure =	pipe_reconfigure,
  289:   .copy_config = 	pipe_copy_config,
  290:   .get_status = 	pipe_get_status,
  291:   .show_proto_info = 	pipe_show_proto_info
  292: };

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>