Annotation of embedaddon/bird2/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, 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>