Return to pipe.c CVS log | Up to [ELWIX - Embedded LightWeight unIX -] / embedaddon / bird / proto / pipe |
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: };