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

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

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