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>