Annotation of embedaddon/bird/proto/pipe/pipe.c, revision 1.1

1.1     ! misho       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, rtable *src_table, net *n, rte *new, rte *old, ea_list *attrs)
        !            48: {
        !            49:   struct pipe_proto *p = (struct pipe_proto *) P;
        !            50:   struct announce_hook *ah = (src_table == P->table) ? p->peer_ahook : P->main_ahook;
        !            51:   rtable *dst_table = ah->table;
        !            52:   struct rte_src *src;
        !            53: 
        !            54:   net *nn;
        !            55:   rte *e;
        !            56:   rta a;
        !            57: 
        !            58:   if (!new && !old)
        !            59:     return;
        !            60: 
        !            61:   if (dst_table->pipe_busy)
        !            62:     {
        !            63:       log(L_ERR "Pipe loop detected when sending %I/%d to table %s",
        !            64:          n->n.prefix, n->n.pxlen, dst_table->name);
        !            65:       return;
        !            66:     }
        !            67: 
        !            68:   nn = net_get(dst_table, n->n.prefix, n->n.pxlen);
        !            69:   if (new)
        !            70:     {
        !            71:       memcpy(&a, new->attrs, sizeof(rta));
        !            72: 
        !            73:       if (p->mode == PIPE_OPAQUE)
        !            74:        {
        !            75:          a.src = P->main_source;
        !            76:          a.source = RTS_PIPE;
        !            77:        }
        !            78: 
        !            79:       a.aflags = 0;
        !            80:       a.eattrs = attrs;
        !            81:       a.hostentry = NULL;
        !            82:       e = rte_get_temp(&a);
        !            83:       e->net = nn;
        !            84:       e->pflags = 0;
        !            85: 
        !            86:       if (p->mode == PIPE_TRANSPARENT)
        !            87:        {
        !            88:          /* Copy protocol specific embedded attributes. */
        !            89:          memcpy(&(e->u), &(new->u), sizeof(e->u));
        !            90:          e->pref = new->pref;
        !            91:          e->pflags = new->pflags;
        !            92:        }
        !            93: 
        !            94:       src = a.src;
        !            95:     }
        !            96:   else
        !            97:     {
        !            98:       e = NULL;
        !            99:       src = old->attrs->src;
        !           100:     }
        !           101: 
        !           102:   src_table->pipe_busy = 1;
        !           103:   rte_update2(ah, nn, e, src);
        !           104:   src_table->pipe_busy = 0;
        !           105: }
        !           106: 
        !           107: static int
        !           108: pipe_import_control(struct proto *P, rte **ee, ea_list **ea UNUSED, struct linpool *p UNUSED)
        !           109: {
        !           110:   struct proto *pp = (*ee)->sender->proto;
        !           111: 
        !           112:   if (pp == P)
        !           113:     return -1; /* Avoid local loops automatically */
        !           114:   return 0;
        !           115: }
        !           116: 
        !           117: static int
        !           118: pipe_reload_routes(struct proto *P)
        !           119: {
        !           120:   struct pipe_proto *p = (struct pipe_proto *) P;
        !           121: 
        !           122:   /*
        !           123:    * Because the pipe protocol feeds routes from both routing tables
        !           124:    * together, both directions are reloaded during refeed and 'reload
        !           125:    * out' command works like 'reload' command. For symmetry, we also
        !           126:    * request refeed when 'reload in' command is used.
        !           127:    */
        !           128:   proto_request_feeding(P);
        !           129: 
        !           130:   proto_reset_limit(P->main_ahook->in_limit);
        !           131:   proto_reset_limit(p->peer_ahook->in_limit);
        !           132: 
        !           133:   return 1;
        !           134: }
        !           135: 
        !           136: static struct proto *
        !           137: pipe_init(struct proto_config *C)
        !           138: {
        !           139:   struct pipe_config *c = (struct pipe_config *) C;
        !           140:   struct proto *P = proto_new(C, sizeof(struct pipe_proto));
        !           141:   struct pipe_proto *p = (struct pipe_proto *) P;
        !           142: 
        !           143:   p->mode = c->mode;
        !           144:   p->peer_table = c->peer->table;
        !           145:   P->accept_ra_types = (p->mode == PIPE_OPAQUE) ? RA_OPTIMAL : RA_ANY;
        !           146:   P->rt_notify = pipe_rt_notify;
        !           147:   P->import_control = pipe_import_control;
        !           148:   P->reload_routes = pipe_reload_routes;
        !           149: 
        !           150:   return P;
        !           151: }
        !           152: 
        !           153: static int
        !           154: pipe_start(struct proto *P)
        !           155: {
        !           156:   struct pipe_config *cf = (struct pipe_config *) P->cf;
        !           157:   struct pipe_proto *p = (struct pipe_proto *) P;
        !           158: 
        !           159:   /* Lock both tables, unlock is handled in pipe_cleanup() */
        !           160:   rt_lock_table(P->table);
        !           161:   rt_lock_table(p->peer_table);
        !           162: 
        !           163:   /* Going directly to PS_UP - prepare for feeding,
        !           164:      connect the protocol to both routing tables */
        !           165: 
        !           166:   P->main_ahook = proto_add_announce_hook(P, P->table, &P->stats);
        !           167:   P->main_ahook->out_filter = cf->c.out_filter;
        !           168:   P->main_ahook->in_limit = cf->c.in_limit;
        !           169:   proto_reset_limit(P->main_ahook->in_limit);
        !           170: 
        !           171:   p->peer_ahook = proto_add_announce_hook(P, p->peer_table, &p->peer_stats);
        !           172:   p->peer_ahook->out_filter = cf->c.in_filter;
        !           173:   p->peer_ahook->in_limit = cf->c.out_limit;
        !           174:   proto_reset_limit(p->peer_ahook->in_limit);
        !           175: 
        !           176:   if (p->mode == PIPE_OPAQUE)
        !           177:     {
        !           178:       P->main_source = rt_get_source(P, 0);
        !           179:       rt_lock_source(P->main_source);
        !           180:     }
        !           181: 
        !           182:   return PS_UP;
        !           183: }
        !           184: 
        !           185: static void
        !           186: pipe_cleanup(struct proto *P)
        !           187: {
        !           188:   struct pipe_proto *p = (struct pipe_proto *) P;
        !           189: 
        !           190:   bzero(&P->stats, sizeof(struct proto_stats));
        !           191:   bzero(&p->peer_stats, sizeof(struct proto_stats));
        !           192: 
        !           193:   P->main_ahook = NULL;
        !           194:   p->peer_ahook = NULL;
        !           195: 
        !           196:   if (p->mode == PIPE_OPAQUE)
        !           197:     rt_unlock_source(P->main_source);
        !           198:   P->main_source = NULL;
        !           199: 
        !           200:   rt_unlock_table(P->table);
        !           201:   rt_unlock_table(p->peer_table);
        !           202: }
        !           203: 
        !           204: static void
        !           205: pipe_postconfig(struct proto_config *C)
        !           206: {
        !           207:   struct pipe_config *c = (struct pipe_config *) C;
        !           208: 
        !           209:   if (!c->peer)
        !           210:     cf_error("Name of peer routing table not specified");
        !           211:   if (c->peer == C->table)
        !           212:     cf_error("Primary table and peer table must be different");
        !           213: 
        !           214:   if (C->in_keep_filtered)
        !           215:     cf_error("Pipe protocol prohibits keeping filtered routes");
        !           216:   if (C->rx_limit)
        !           217:     cf_error("Pipe protocol does not support receive limits");
        !           218: }
        !           219: 
        !           220: extern int proto_reconfig_type;
        !           221: 
        !           222: static int
        !           223: pipe_reconfigure(struct proto *P, struct proto_config *new)
        !           224: {
        !           225:   struct pipe_proto *p = (struct pipe_proto *)P;
        !           226:   struct proto_config *old = P->cf;
        !           227:   struct pipe_config *oc = (struct pipe_config *) old;
        !           228:   struct pipe_config *nc = (struct pipe_config *) new;
        !           229: 
        !           230:   if ((oc->peer->table != nc->peer->table) || (oc->mode != nc->mode))
        !           231:     return 0;
        !           232: 
        !           233:   /* Update output filters in ahooks */
        !           234:   if (P->main_ahook)
        !           235:     {
        !           236:       P->main_ahook->out_filter = new->out_filter;
        !           237:       P->main_ahook->in_limit = new->in_limit;
        !           238:       proto_verify_limits(P->main_ahook);
        !           239:     }
        !           240: 
        !           241:   if (p->peer_ahook)
        !           242:     {
        !           243:       p->peer_ahook->out_filter = new->in_filter;
        !           244:       p->peer_ahook->in_limit = new->out_limit;
        !           245:       proto_verify_limits(p->peer_ahook);
        !           246:     }
        !           247: 
        !           248:   if ((P->proto_state != PS_UP) || (proto_reconfig_type == RECONFIG_SOFT))
        !           249:     return 1;
        !           250: 
        !           251:   if ((new->preference != old->preference)
        !           252:       || ! filter_same(new->in_filter, old->in_filter)
        !           253:       || ! filter_same(new->out_filter, old->out_filter))
        !           254:     proto_request_feeding(P);
        !           255: 
        !           256:   return 1;
        !           257: }
        !           258: 
        !           259: static void
        !           260: pipe_copy_config(struct proto_config *dest, struct proto_config *src)
        !           261: {
        !           262:   /* Just a shallow copy, not many items here */
        !           263:   proto_copy_rest(dest, src, sizeof(struct pipe_config));
        !           264: }
        !           265: 
        !           266: static void
        !           267: pipe_get_status(struct proto *P, byte *buf)
        !           268: {
        !           269:   struct pipe_proto *p = (struct pipe_proto *) P;
        !           270: 
        !           271:   bsprintf(buf, "%c> %s", (p->mode == PIPE_OPAQUE) ? '-' : '=', p->peer_table->name);
        !           272: }
        !           273: 
        !           274: static void
        !           275: pipe_show_stats(struct pipe_proto *p)
        !           276: {
        !           277:   struct proto_stats *s1 = &p->p.stats;
        !           278:   struct proto_stats *s2 = &p->peer_stats;
        !           279: 
        !           280:   /*
        !           281:    * Pipe stats (as anything related to pipes) are a bit tricky. There
        !           282:    * are two sets of stats - s1 for ahook to the primary routing and
        !           283:    * s2 for the ahook to the secondary routing table. The user point
        !           284:    * of view is that routes going from the primary routing table to
        !           285:    * the secondary routing table are 'exported', while routes going in
        !           286:    * the other direction are 'imported'.
        !           287:    *
        !           288:    * Each route going through a pipe is, technically, first exported
        !           289:    * to the pipe and then imported from that pipe and such operations
        !           290:    * are counted in one set of stats according to the direction of the
        !           291:    * route propagation. Filtering is done just in the first part
        !           292:    * (export). Therefore, we compose stats for one directon for one
        !           293:    * user direction from both import and export stats, skipping
        !           294:    * immediate and irrelevant steps (exp_updates_accepted,
        !           295:    * imp_updates_received, imp_updates_filtered, ...).
        !           296:    *
        !           297:    * Rule of thumb is that stats s1 have the correct 'polarity'
        !           298:    * (imp/exp), while stats s2 have switched 'polarity'.
        !           299:    */
        !           300: 
        !           301:   cli_msg(-1006, "  Routes:         %u imported, %u exported",
        !           302:          s1->imp_routes, s2->imp_routes);
        !           303:   cli_msg(-1006, "  Route change stats:     received   rejected   filtered    ignored   accepted");
        !           304:   cli_msg(-1006, "    Import updates:     %10u %10u %10u %10u %10u",
        !           305:          s2->exp_updates_received, s2->exp_updates_rejected + s1->imp_updates_invalid,
        !           306:          s2->exp_updates_filtered, s1->imp_updates_ignored, s1->imp_updates_accepted);
        !           307:   cli_msg(-1006, "    Import withdraws:   %10u %10u        --- %10u %10u",
        !           308:          s2->exp_withdraws_received, s1->imp_withdraws_invalid,
        !           309:          s1->imp_withdraws_ignored, s1->imp_withdraws_accepted);
        !           310:   cli_msg(-1006, "    Export updates:     %10u %10u %10u %10u %10u",
        !           311:          s1->exp_updates_received, s1->exp_updates_rejected + s2->imp_updates_invalid,
        !           312:          s1->exp_updates_filtered, s2->imp_updates_ignored, s2->imp_updates_accepted);
        !           313:   cli_msg(-1006, "    Export withdraws:   %10u %10u        --- %10u %10u",
        !           314:          s1->exp_withdraws_received, s2->imp_withdraws_invalid,
        !           315:          s2->imp_withdraws_ignored, s2->imp_withdraws_accepted);
        !           316: }
        !           317: 
        !           318: static void
        !           319: pipe_show_proto_info(struct proto *P)
        !           320: {
        !           321:   struct pipe_proto *p = (struct pipe_proto *) P;
        !           322:   struct pipe_config *cf = (struct pipe_config *) P->cf;
        !           323: 
        !           324:   // cli_msg(-1006, "  Table:          %s", P->table->name);
        !           325:   // cli_msg(-1006, "  Peer table:     %s", p->peer_table->name);
        !           326:   cli_msg(-1006, "  Preference:     %d", P->preference);
        !           327:   cli_msg(-1006, "  Input filter:   %s", filter_name(cf->c.in_filter));
        !           328:   cli_msg(-1006, "  Output filter:  %s", filter_name(cf->c.out_filter));
        !           329: 
        !           330:   proto_show_limit(cf->c.in_limit, "Import limit:");
        !           331:   proto_show_limit(cf->c.out_limit, "Export limit:");
        !           332: 
        !           333:   if (P->proto_state != PS_DOWN)
        !           334:     pipe_show_stats(p);
        !           335: }
        !           336: 
        !           337: 
        !           338: struct protocol proto_pipe = {
        !           339:   .name =              "Pipe",
        !           340:   .template =          "pipe%d",
        !           341:   .multitable =                1,
        !           342:   .preference =                DEF_PREF_PIPE,
        !           343:   .config_size =       sizeof(struct pipe_config),
        !           344:   .postconfig =                pipe_postconfig,
        !           345:   .init =              pipe_init,
        !           346:   .start =             pipe_start,
        !           347:   .cleanup =           pipe_cleanup,
        !           348:   .reconfigure =       pipe_reconfigure,
        !           349:   .copy_config =       pipe_copy_config,
        !           350:   .get_status =        pipe_get_status,
        !           351:   .show_proto_info =   pipe_show_proto_info
        !           352: };

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