Return to rip.c CVS log | Up to [ELWIX - Embedded LightWeight unIX -] / embedaddon / bird2 / proto / rip |
1.1 ! misho 1: /* ! 2: * BIRD -- Routing Information Protocol (RIP) ! 3: * ! 4: * (c) 1998--1999 Pavel Machek <pavel@ucw.cz> ! 5: * (c) 2004--2013 Ondrej Filip <feela@network.cz> ! 6: * (c) 2009--2015 Ondrej Zajicek <santiago@crfreenet.org> ! 7: * (c) 2009--2015 CZ.NIC z.s.p.o. ! 8: * ! 9: * Can be freely distributed and used under the terms of the GNU GPL. ! 10: */ ! 11: ! 12: /** ! 13: * DOC: Routing Information Protocol (RIP) ! 14: * ! 15: * The RIP protocol is implemented in two files: |rip.c| containing the protocol ! 16: * logic, route management and the protocol glue with BIRD core, and |packets.c| ! 17: * handling RIP packet processing, RX, TX and protocol sockets. ! 18: * ! 19: * Each instance of RIP is described by a structure &rip_proto, which contains ! 20: * an internal RIP routing table, a list of protocol interfaces and the main ! 21: * timer responsible for RIP routing table cleanup. ! 22: * ! 23: * RIP internal routing table contains incoming and outgoing routes. For each ! 24: * network (represented by structure &rip_entry) there is one outgoing route ! 25: * stored directly in &rip_entry and an one-way linked list of incoming routes ! 26: * (structures &rip_rte). The list contains incoming routes from different RIP ! 27: * neighbors, but only routes with the lowest metric are stored (i.e., all ! 28: * stored incoming routes have the same metric). ! 29: * ! 30: * Note that RIP itself does not select outgoing route, that is done by the core ! 31: * routing table. When a new incoming route is received, it is propagated to the ! 32: * RIP table by rip_update_rte() and possibly stored in the list of incoming ! 33: * routes. Then the change may be propagated to the core by rip_announce_rte(). ! 34: * The core selects the best route and propagate it to RIP by rip_rt_notify(), ! 35: * which updates outgoing route part of &rip_entry and possibly triggers route ! 36: * propagation by rip_trigger_update(). ! 37: * ! 38: * RIP interfaces are represented by structures &rip_iface. A RIP interface ! 39: * contains a per-interface socket, a list of associated neighbors, interface ! 40: * configuration, and state information related to scheduled interface events ! 41: * and running update sessions. RIP interfaces are added and removed based on ! 42: * core interface notifications. ! 43: * ! 44: * There are two RIP interface events - regular updates and triggered updates. ! 45: * Both are managed from the RIP interface timer (rip_iface_timer()). Regular ! 46: * updates are called at fixed interval and propagate the whole routing table, ! 47: * while triggered updates are scheduled by rip_trigger_update() due to some ! 48: * routing table change and propagate only the routes modified since the time ! 49: * they were scheduled. There are also unicast-destined requested updates, but ! 50: * these are sent directly as a reaction to received RIP request message. The ! 51: * update session is started by rip_send_table(). There may be at most one ! 52: * active update session per interface, as the associated state (including the ! 53: * fib iterator) is stored directly in &rip_iface structure. ! 54: * ! 55: * RIP neighbors are represented by structures &rip_neighbor. Compared to ! 56: * neighbor handling in other routing protocols, RIP does not have explicit ! 57: * neighbor discovery and adjacency maintenance, which makes the &rip_neighbor ! 58: * related code a bit peculiar. RIP neighbors are interlinked with core neighbor ! 59: * structures (&neighbor) and use core neighbor notifications to ensure that RIP ! 60: * neighbors are timely removed. RIP neighbors are added based on received route ! 61: * notifications and removed based on core neighbor and RIP interface events. ! 62: * ! 63: * RIP neighbors are linked by RIP routes and use counter to track the number of ! 64: * associated routes, but when these RIP routes timeout, associated RIP neighbor ! 65: * is still alive (with zero counter). When RIP neighbor is removed but still ! 66: * has some associated routes, it is not freed, just changed to detached state ! 67: * (core neighbors and RIP ifaces are unlinked), then during the main timer ! 68: * cleanup phase the associated routes are removed and the &rip_neighbor ! 69: * structure is finally freed. ! 70: * ! 71: * Supported standards: ! 72: * - RFC 1058 - RIPv1 ! 73: * - RFC 2453 - RIPv2 ! 74: * - RFC 2080 - RIPng ! 75: * - RFC 4822 - RIP cryptographic authentication ! 76: */ ! 77: ! 78: #include <stdlib.h> ! 79: #include "rip.h" ! 80: ! 81: ! 82: static inline void rip_lock_neighbor(struct rip_neighbor *n); ! 83: static inline void rip_unlock_neighbor(struct rip_neighbor *n); ! 84: static inline int rip_iface_link_up(struct rip_iface *ifa); ! 85: static inline void rip_kick_timer(struct rip_proto *p); ! 86: static inline void rip_iface_kick_timer(struct rip_iface *ifa); ! 87: static void rip_iface_timer(timer *timer); ! 88: static void rip_trigger_update(struct rip_proto *p); ! 89: ! 90: ! 91: /* ! 92: * RIP routes ! 93: */ ! 94: ! 95: static struct rip_rte * ! 96: rip_add_rte(struct rip_proto *p, struct rip_rte **rp, struct rip_rte *src) ! 97: { ! 98: struct rip_rte *rt = sl_alloc(p->rte_slab); ! 99: ! 100: memcpy(rt, src, sizeof(struct rip_rte)); ! 101: rt->next = *rp; ! 102: *rp = rt; ! 103: ! 104: rip_lock_neighbor(rt->from); ! 105: ! 106: return rt; ! 107: } ! 108: ! 109: static inline void ! 110: rip_remove_rte(struct rip_proto *p, struct rip_rte **rp) ! 111: { ! 112: struct rip_rte *rt = *rp; ! 113: ! 114: rip_unlock_neighbor(rt->from); ! 115: ! 116: *rp = rt->next; ! 117: sl_free(p->rte_slab, rt); ! 118: } ! 119: ! 120: static inline int rip_same_rte(struct rip_rte *a, struct rip_rte *b) ! 121: { return a->metric == b->metric && a->tag == b->tag && ipa_equal(a->next_hop, b->next_hop); } ! 122: ! 123: static inline int rip_valid_rte(struct rip_rte *rt) ! 124: { return rt->from->ifa != NULL; } ! 125: ! 126: /** ! 127: * rip_announce_rte - announce route from RIP routing table to the core ! 128: * @p: RIP instance ! 129: * @en: related network ! 130: * ! 131: * The function takes a list of incoming routes from @en, prepare appropriate ! 132: * &rte for the core and propagate it by rte_update(). ! 133: */ ! 134: static void ! 135: rip_announce_rte(struct rip_proto *p, struct rip_entry *en) ! 136: { ! 137: struct rip_rte *rt = en->routes; ! 138: ! 139: /* Find first valid rte */ ! 140: while (rt && !rip_valid_rte(rt)) ! 141: rt = rt->next; ! 142: ! 143: if (rt) ! 144: { ! 145: /* Update */ ! 146: rta a0 = { ! 147: .src = p->p.main_source, ! 148: .source = RTS_RIP, ! 149: .scope = SCOPE_UNIVERSE, ! 150: .dest = RTD_UNICAST, ! 151: }; ! 152: ! 153: u8 rt_metric = rt->metric; ! 154: u16 rt_tag = rt->tag; ! 155: ! 156: if (p->ecmp) ! 157: { ! 158: /* ECMP route */ ! 159: struct nexthop *nhs = NULL; ! 160: int num = 0; ! 161: ! 162: for (rt = en->routes; rt && (num < p->ecmp); rt = rt->next) ! 163: { ! 164: if (!rip_valid_rte(rt)) ! 165: continue; ! 166: ! 167: struct nexthop *nh = allocz(sizeof(struct nexthop)); ! 168: ! 169: nh->gw = rt->next_hop; ! 170: nh->iface = rt->from->nbr->iface; ! 171: nh->weight = rt->from->ifa->cf->ecmp_weight; ! 172: ! 173: nexthop_insert(&nhs, nh); ! 174: num++; ! 175: ! 176: if (rt->tag != rt_tag) ! 177: rt_tag = 0; ! 178: } ! 179: ! 180: a0.nh = *nhs; ! 181: } ! 182: else ! 183: { ! 184: /* Unipath route */ ! 185: a0.from = rt->from->nbr->addr; ! 186: a0.nh.gw = rt->next_hop; ! 187: a0.nh.iface = rt->from->nbr->iface; ! 188: } ! 189: ! 190: rta *a = rta_lookup(&a0); ! 191: rte *e = rte_get_temp(a); ! 192: ! 193: e->u.rip.from = a0.nh.iface; ! 194: e->u.rip.metric = rt_metric; ! 195: e->u.rip.tag = rt_tag; ! 196: e->pflags = EA_ID_FLAG(EA_RIP_METRIC) | EA_ID_FLAG(EA_RIP_TAG); ! 197: ! 198: rte_update(&p->p, en->n.addr, e); ! 199: } ! 200: else ! 201: { ! 202: /* Withdraw */ ! 203: rte_update(&p->p, en->n.addr, NULL); ! 204: } ! 205: } ! 206: ! 207: /** ! 208: * rip_update_rte - enter a route update to RIP routing table ! 209: * @p: RIP instance ! 210: * @addr: network address ! 211: * @new: a &rip_rte representing the new route ! 212: * ! 213: * The function is called by the RIP packet processing code whenever it receives ! 214: * a reachable route. The appropriate routing table entry is found and the list ! 215: * of incoming routes is updated. Eventually, the change is also propagated to ! 216: * the core by rip_announce_rte(). Note that for unreachable routes, ! 217: * rip_withdraw_rte() should be called instead of rip_update_rte(). ! 218: */ ! 219: void ! 220: rip_update_rte(struct rip_proto *p, net_addr *n, struct rip_rte *new) ! 221: { ! 222: struct rip_entry *en = fib_get(&p->rtable, n); ! 223: struct rip_rte *rt, **rp; ! 224: int changed = 0; ! 225: ! 226: /* If the new route is better, remove all current routes */ ! 227: if (en->routes && new->metric < en->routes->metric) ! 228: while (en->routes) ! 229: rip_remove_rte(p, &en->routes); ! 230: ! 231: /* Find the old route (also set rp for later) */ ! 232: for (rp = &en->routes; rt = *rp; rp = &rt->next) ! 233: if (rt->from == new->from) ! 234: { ! 235: if (rip_same_rte(rt, new)) ! 236: { ! 237: rt->expires = new->expires; ! 238: return; ! 239: } ! 240: ! 241: /* Remove the old route */ ! 242: rip_remove_rte(p, rp); ! 243: changed = 1; ! 244: break; ! 245: } ! 246: ! 247: /* If the new route is optimal, add it to the list */ ! 248: if (!en->routes || new->metric == en->routes->metric) ! 249: { ! 250: rt = rip_add_rte(p, rp, new); ! 251: changed = 1; ! 252: } ! 253: ! 254: /* Announce change if on relevant position (the first or any for ECMP) */ ! 255: if (changed && (rp == &en->routes || p->ecmp)) ! 256: rip_announce_rte(p, en); ! 257: } ! 258: ! 259: /** ! 260: * rip_withdraw_rte - enter a route withdraw to RIP routing table ! 261: * @p: RIP instance ! 262: * @addr: network address ! 263: * @from: a &rip_neighbor propagating the withdraw ! 264: * ! 265: * The function is called by the RIP packet processing code whenever it receives ! 266: * an unreachable route. The incoming route for given network from nbr @from is ! 267: * removed. Eventually, the change is also propagated by rip_announce_rte(). ! 268: */ ! 269: void ! 270: rip_withdraw_rte(struct rip_proto *p, net_addr *n, struct rip_neighbor *from) ! 271: { ! 272: struct rip_entry *en = fib_find(&p->rtable, n); ! 273: struct rip_rte *rt, **rp; ! 274: ! 275: if (!en) ! 276: return; ! 277: ! 278: /* Find the old route */ ! 279: for (rp = &en->routes; rt = *rp; rp = &rt->next) ! 280: if (rt->from == from) ! 281: break; ! 282: ! 283: if (!rt) ! 284: return; ! 285: ! 286: /* Remove the old route */ ! 287: rip_remove_rte(p, rp); ! 288: ! 289: /* Announce change if on relevant position */ ! 290: if (rp == &en->routes || p->ecmp) ! 291: rip_announce_rte(p, en); ! 292: } ! 293: ! 294: /* ! 295: * rip_rt_notify - core tells us about new route, so store ! 296: * it into our data structures. ! 297: */ ! 298: static void ! 299: rip_rt_notify(struct proto *P, struct channel *ch UNUSED, struct network *net, struct rte *new, ! 300: struct rte *old UNUSED) ! 301: { ! 302: struct rip_proto *p = (struct rip_proto *) P; ! 303: struct rip_entry *en; ! 304: int old_metric; ! 305: ! 306: if (new) ! 307: { ! 308: /* Update */ ! 309: u32 rt_metric = ea_get_int(new->attrs->eattrs, EA_RIP_METRIC, 1); ! 310: u32 rt_tag = ea_get_int(new->attrs->eattrs, EA_RIP_TAG, 0); ! 311: ! 312: if (rt_metric > p->infinity) ! 313: { ! 314: log(L_WARN "%s: Invalid rip_metric value %u for route %N", ! 315: p->p.name, rt_metric, net->n.addr); ! 316: rt_metric = p->infinity; ! 317: } ! 318: ! 319: if (rt_tag > 0xffff) ! 320: { ! 321: log(L_WARN "%s: Invalid rip_tag value %u for route %N", ! 322: p->p.name, rt_tag, net->n.addr); ! 323: rt_metric = p->infinity; ! 324: rt_tag = 0; ! 325: } ! 326: ! 327: /* ! 328: * Note that we accept exported routes with infinity metric (this could ! 329: * happen if rip_metric is modified in filters). Such entry has infinity ! 330: * metric but is RIP_ENTRY_VALID and therefore is not subject to garbage ! 331: * collection. ! 332: */ ! 333: ! 334: en = fib_get(&p->rtable, net->n.addr); ! 335: ! 336: old_metric = en->valid ? en->metric : -1; ! 337: ! 338: en->valid = RIP_ENTRY_VALID; ! 339: en->metric = rt_metric; ! 340: en->tag = rt_tag; ! 341: en->from = (new->attrs->src->proto == P) ? new->u.rip.from : NULL; ! 342: en->iface = new->attrs->nh.iface; ! 343: en->next_hop = new->attrs->nh.gw; ! 344: } ! 345: else ! 346: { ! 347: /* Withdraw */ ! 348: en = fib_find(&p->rtable, net->n.addr); ! 349: ! 350: if (!en || en->valid != RIP_ENTRY_VALID) ! 351: return; ! 352: ! 353: old_metric = en->metric; ! 354: ! 355: en->valid = RIP_ENTRY_STALE; ! 356: en->metric = p->infinity; ! 357: en->tag = 0; ! 358: en->from = NULL; ! 359: en->iface = NULL; ! 360: en->next_hop = IPA_NONE; ! 361: } ! 362: ! 363: /* Activate triggered updates */ ! 364: if (en->metric != old_metric) ! 365: { ! 366: en->changed = current_time(); ! 367: rip_trigger_update(p); ! 368: } ! 369: } ! 370: ! 371: ! 372: /* ! 373: * RIP neighbors ! 374: */ ! 375: ! 376: struct rip_neighbor * ! 377: rip_get_neighbor(struct rip_proto *p, ip_addr *a, struct rip_iface *ifa) ! 378: { ! 379: neighbor *nbr = neigh_find(&p->p, *a, ifa->iface, 0); ! 380: ! 381: if (!nbr || (nbr->scope == SCOPE_HOST) || !rip_iface_link_up(ifa)) ! 382: return NULL; ! 383: ! 384: if (nbr->data) ! 385: return nbr->data; ! 386: ! 387: TRACE(D_EVENTS, "New neighbor %I on %s", *a, ifa->iface->name); ! 388: ! 389: struct rip_neighbor *n = mb_allocz(p->p.pool, sizeof(struct rip_neighbor)); ! 390: n->ifa = ifa; ! 391: n->nbr = nbr; ! 392: nbr->data = n; ! 393: n->csn = nbr->aux; ! 394: ! 395: add_tail(&ifa->neigh_list, NODE n); ! 396: ! 397: return n; ! 398: } ! 399: ! 400: static void ! 401: rip_remove_neighbor(struct rip_proto *p, struct rip_neighbor *n) ! 402: { ! 403: neighbor *nbr = n->nbr; ! 404: ! 405: TRACE(D_EVENTS, "Removing neighbor %I on %s", nbr->addr, nbr->iface->name); ! 406: ! 407: rem_node(NODE n); ! 408: n->ifa = NULL; ! 409: n->nbr = NULL; ! 410: nbr->data = NULL; ! 411: nbr->aux = n->csn; ! 412: ! 413: rfree(n->bfd_req); ! 414: n->bfd_req = NULL; ! 415: n->last_seen = 0; ! 416: ! 417: if (!n->uc) ! 418: mb_free(n); ! 419: ! 420: /* Related routes are removed in rip_timer() */ ! 421: rip_kick_timer(p); ! 422: } ! 423: ! 424: static inline void ! 425: rip_lock_neighbor(struct rip_neighbor *n) ! 426: { ! 427: n->uc++; ! 428: } ! 429: ! 430: static inline void ! 431: rip_unlock_neighbor(struct rip_neighbor *n) ! 432: { ! 433: n->uc--; ! 434: ! 435: if (!n->nbr && !n->uc) ! 436: mb_free(n); ! 437: } ! 438: ! 439: static void ! 440: rip_neigh_notify(struct neighbor *nbr) ! 441: { ! 442: struct rip_proto *p = (struct rip_proto *) nbr->proto; ! 443: struct rip_neighbor *n = nbr->data; ! 444: ! 445: if (!n) ! 446: return; ! 447: ! 448: /* ! 449: * We assume that rip_neigh_notify() is called before rip_if_notify() for ! 450: * IF_CHANGE_DOWN and therefore n->ifa is still valid. We have no such ! 451: * ordering assumption for IF_CHANGE_LINK, so we test link state of the ! 452: * underlying iface instead of just rip_iface state. ! 453: */ ! 454: if ((nbr->scope <= 0) || !rip_iface_link_up(n->ifa)) ! 455: rip_remove_neighbor(p, n); ! 456: } ! 457: ! 458: static void ! 459: rip_bfd_notify(struct bfd_request *req) ! 460: { ! 461: struct rip_neighbor *n = req->data; ! 462: struct rip_proto *p = n->ifa->rip; ! 463: ! 464: if (req->down) ! 465: { ! 466: TRACE(D_EVENTS, "BFD session down for nbr %I on %s", ! 467: n->nbr->addr, n->ifa->iface->name); ! 468: rip_remove_neighbor(p, n); ! 469: } ! 470: } ! 471: ! 472: void ! 473: rip_update_bfd(struct rip_proto *p, struct rip_neighbor *n) ! 474: { ! 475: int use_bfd = n->ifa->cf->bfd && n->last_seen; ! 476: ! 477: if (use_bfd && !n->bfd_req) ! 478: { ! 479: /* ! 480: * For RIPv2, use the same address as rip_open_socket(). For RIPng, neighbor ! 481: * should contain an address from the same prefix, thus also link-local. It ! 482: * may cause problems if two link-local addresses are assigned to one iface. ! 483: */ ! 484: ip_addr saddr = rip_is_v2(p) ? n->ifa->sk->saddr : n->nbr->ifa->ip; ! 485: n->bfd_req = bfd_request_session(p->p.pool, n->nbr->addr, saddr, ! 486: n->nbr->iface, p->p.vrf, ! 487: rip_bfd_notify, n); ! 488: } ! 489: ! 490: if (!use_bfd && n->bfd_req) ! 491: { ! 492: rfree(n->bfd_req); ! 493: n->bfd_req = NULL; ! 494: } ! 495: } ! 496: ! 497: ! 498: /* ! 499: * RIP interfaces ! 500: */ ! 501: ! 502: static void ! 503: rip_iface_start(struct rip_iface *ifa) ! 504: { ! 505: struct rip_proto *p = ifa->rip; ! 506: ! 507: TRACE(D_EVENTS, "Starting interface %s", ifa->iface->name); ! 508: ! 509: ifa->next_regular = current_time() + (random() % ifa->cf->update_time) + 100 MS; ! 510: ifa->next_triggered = current_time(); /* Available immediately */ ! 511: ifa->want_triggered = 1; /* All routes in triggered update */ ! 512: tm_start(ifa->timer, 100 MS); ! 513: ifa->up = 1; ! 514: ! 515: if (!ifa->cf->passive) ! 516: rip_send_request(ifa->rip, ifa); ! 517: } ! 518: ! 519: static void ! 520: rip_iface_stop(struct rip_iface *ifa) ! 521: { ! 522: struct rip_proto *p = ifa->rip; ! 523: struct rip_neighbor *n; ! 524: ! 525: TRACE(D_EVENTS, "Stopping interface %s", ifa->iface->name); ! 526: ! 527: rip_reset_tx_session(p, ifa); ! 528: ! 529: WALK_LIST_FIRST(n, ifa->neigh_list) ! 530: rip_remove_neighbor(p, n); ! 531: ! 532: tm_stop(ifa->timer); ! 533: ifa->up = 0; ! 534: } ! 535: ! 536: static inline int ! 537: rip_iface_link_up(struct rip_iface *ifa) ! 538: { ! 539: return !ifa->cf->check_link || (ifa->iface->flags & IF_LINK_UP); ! 540: } ! 541: ! 542: static void ! 543: rip_iface_update_state(struct rip_iface *ifa) ! 544: { ! 545: int up = ifa->sk && rip_iface_link_up(ifa); ! 546: ! 547: if (up == ifa->up) ! 548: return; ! 549: ! 550: if (up) ! 551: rip_iface_start(ifa); ! 552: else ! 553: rip_iface_stop(ifa); ! 554: } ! 555: ! 556: static void ! 557: rip_iface_update_buffers(struct rip_iface *ifa) ! 558: { ! 559: if (!ifa->sk) ! 560: return; ! 561: ! 562: uint rbsize = ifa->cf->rx_buffer ?: ifa->iface->mtu; ! 563: uint tbsize = ifa->cf->tx_length ?: ifa->iface->mtu; ! 564: rbsize = MAX(rbsize, tbsize); ! 565: ! 566: sk_set_rbsize(ifa->sk, rbsize); ! 567: sk_set_tbsize(ifa->sk, tbsize); ! 568: ! 569: uint headers = (rip_is_v2(ifa->rip) ? IP4_HEADER_LENGTH : IP6_HEADER_LENGTH) + UDP_HEADER_LENGTH; ! 570: ifa->tx_plen = tbsize - headers; ! 571: ! 572: if (ifa->cf->auth_type == RIP_AUTH_CRYPTO) ! 573: ifa->tx_plen -= RIP_AUTH_TAIL_LENGTH + max_mac_length(ifa->cf->passwords); ! 574: } ! 575: ! 576: static inline void ! 577: rip_iface_update_bfd(struct rip_iface *ifa) ! 578: { ! 579: struct rip_proto *p = ifa->rip; ! 580: struct rip_neighbor *n; ! 581: ! 582: WALK_LIST(n, ifa->neigh_list) ! 583: rip_update_bfd(p, n); ! 584: } ! 585: ! 586: ! 587: static void ! 588: rip_iface_locked(struct object_lock *lock) ! 589: { ! 590: struct rip_iface *ifa = lock->data; ! 591: struct rip_proto *p = ifa->rip; ! 592: ! 593: if (!rip_open_socket(ifa)) ! 594: { ! 595: log(L_ERR "%s: Cannot open socket for %s", p->p.name, ifa->iface->name); ! 596: return; ! 597: } ! 598: ! 599: rip_iface_update_buffers(ifa); ! 600: rip_iface_update_state(ifa); ! 601: } ! 602: ! 603: ! 604: static struct rip_iface * ! 605: rip_find_iface(struct rip_proto *p, struct iface *what) ! 606: { ! 607: struct rip_iface *ifa; ! 608: ! 609: WALK_LIST(ifa, p->iface_list) ! 610: if (ifa->iface == what) ! 611: return ifa; ! 612: ! 613: return NULL; ! 614: } ! 615: ! 616: static void ! 617: rip_add_iface(struct rip_proto *p, struct iface *iface, struct rip_iface_config *ic) ! 618: { ! 619: struct rip_iface *ifa; ! 620: ! 621: TRACE(D_EVENTS, "Adding interface %s", iface->name); ! 622: ! 623: ifa = mb_allocz(p->p.pool, sizeof(struct rip_iface)); ! 624: ifa->rip = p; ! 625: ifa->iface = iface; ! 626: ifa->cf = ic; ! 627: ! 628: if (ipa_nonzero(ic->address)) ! 629: ifa->addr = ic->address; ! 630: else if (ic->mode == RIP_IM_MULTICAST) ! 631: ifa->addr = rip_is_v2(p) ? IP4_RIP_ROUTERS : IP6_RIP_ROUTERS; ! 632: else /* Broadcast */ ! 633: ifa->addr = iface->addr4->brd; ! 634: /* ! 635: * The above is just a workaround for BSD as it can't send broadcasts ! 636: * to 255.255.255.255. BSD systems need the network broadcast address instead. ! 637: * ! 638: * TODO: move this to sysdep code ! 639: */ ! 640: ! 641: init_list(&ifa->neigh_list); ! 642: ! 643: add_tail(&p->iface_list, NODE ifa); ! 644: ! 645: ifa->timer = tm_new_init(p->p.pool, rip_iface_timer, ifa, 0, 0); ! 646: ! 647: struct object_lock *lock = olock_new(p->p.pool); ! 648: lock->type = OBJLOCK_UDP; ! 649: lock->port = ic->port; ! 650: lock->iface = iface; ! 651: lock->data = ifa; ! 652: lock->hook = rip_iface_locked; ! 653: ifa->lock = lock; ! 654: ! 655: olock_acquire(lock); ! 656: } ! 657: ! 658: static void ! 659: rip_remove_iface(struct rip_proto *p, struct rip_iface *ifa) ! 660: { ! 661: rip_iface_stop(ifa); ! 662: ! 663: TRACE(D_EVENTS, "Removing interface %s", ifa->iface->name); ! 664: ! 665: rem_node(NODE ifa); ! 666: ! 667: rfree(ifa->sk); ! 668: rfree(ifa->lock); ! 669: rfree(ifa->timer); ! 670: ! 671: mb_free(ifa); ! 672: } ! 673: ! 674: static int ! 675: rip_reconfigure_iface(struct rip_proto *p, struct rip_iface *ifa, struct rip_iface_config *new) ! 676: { ! 677: struct rip_iface_config *old = ifa->cf; ! 678: ! 679: /* Change of these options would require to reset the iface socket */ ! 680: if ((new->mode != old->mode) || ! 681: (new->port != old->port) || ! 682: (new->tx_tos != old->tx_tos) || ! 683: (new->tx_priority != old->tx_priority) || ! 684: (new->ttl_security != old->ttl_security)) ! 685: return 0; ! 686: ! 687: TRACE(D_EVENTS, "Reconfiguring interface %s", ifa->iface->name); ! 688: ! 689: ifa->cf = new; ! 690: ! 691: rip_iface_update_buffers(ifa); ! 692: ! 693: if (ifa->next_regular > (current_time() + new->update_time)) ! 694: ifa->next_regular = current_time() + (random() % new->update_time) + 100 MS; ! 695: ! 696: if (new->check_link != old->check_link) ! 697: rip_iface_update_state(ifa); ! 698: ! 699: if (new->bfd != old->bfd) ! 700: rip_iface_update_bfd(ifa); ! 701: ! 702: if (ifa->up) ! 703: rip_iface_kick_timer(ifa); ! 704: ! 705: return 1; ! 706: } ! 707: ! 708: static void ! 709: rip_reconfigure_ifaces(struct rip_proto *p, struct rip_config *cf) ! 710: { ! 711: struct iface *iface; ! 712: ! 713: WALK_LIST(iface, iface_list) ! 714: { ! 715: if (!(iface->flags & IF_UP)) ! 716: continue; ! 717: ! 718: /* Ignore ifaces without appropriate address */ ! 719: if (rip_is_v2(p) ? !iface->addr4 : !iface->llv6) ! 720: continue; ! 721: ! 722: struct rip_iface *ifa = rip_find_iface(p, iface); ! 723: struct rip_iface_config *ic = (void *) iface_patt_find(&cf->patt_list, iface, NULL); ! 724: ! 725: if (ifa && ic) ! 726: { ! 727: if (rip_reconfigure_iface(p, ifa, ic)) ! 728: continue; ! 729: ! 730: /* Hard restart */ ! 731: log(L_INFO "%s: Restarting interface %s", p->p.name, ifa->iface->name); ! 732: rip_remove_iface(p, ifa); ! 733: rip_add_iface(p, iface, ic); ! 734: } ! 735: ! 736: if (ifa && !ic) ! 737: rip_remove_iface(p, ifa); ! 738: ! 739: if (!ifa && ic) ! 740: rip_add_iface(p, iface, ic); ! 741: } ! 742: } ! 743: ! 744: static void ! 745: rip_if_notify(struct proto *P, unsigned flags, struct iface *iface) ! 746: { ! 747: struct rip_proto *p = (void *) P; ! 748: struct rip_config *cf = (void *) P->cf; ! 749: struct rip_iface *ifa = rip_find_iface(p, iface); ! 750: ! 751: if (iface->flags & IF_IGNORE) ! 752: return; ! 753: ! 754: /* Add, remove or restart interface */ ! 755: if (flags & (IF_CHANGE_UPDOWN | (rip_is_v2(p) ? IF_CHANGE_ADDR4 : IF_CHANGE_LLV6))) ! 756: { ! 757: if (ifa) ! 758: rip_remove_iface(p, ifa); ! 759: ! 760: if (!(iface->flags & IF_UP)) ! 761: return; ! 762: ! 763: /* Ignore ifaces without appropriate address */ ! 764: if (rip_is_v2(p) ? !iface->addr4 : !iface->llv6) ! 765: return; ! 766: ! 767: struct rip_iface_config *ic = (void *) iface_patt_find(&cf->patt_list, iface, NULL); ! 768: if (ic) ! 769: rip_add_iface(p, iface, ic); ! 770: ! 771: return; ! 772: } ! 773: ! 774: if (!ifa) ! 775: return; ! 776: ! 777: if (flags & IF_CHANGE_MTU) ! 778: rip_iface_update_buffers(ifa); ! 779: ! 780: if (flags & IF_CHANGE_LINK) ! 781: rip_iface_update_state(ifa); ! 782: } ! 783: ! 784: ! 785: /* ! 786: * RIP timer events ! 787: */ ! 788: ! 789: /** ! 790: * rip_timer - RIP main timer hook ! 791: * @t: timer ! 792: * ! 793: * The RIP main timer is responsible for routing table maintenance. Invalid or ! 794: * expired routes (&rip_rte) are removed and garbage collection of stale routing ! 795: * table entries (&rip_entry) is done. Changes are propagated to core tables, ! 796: * route reload is also done here. Note that garbage collection uses a maximal ! 797: * GC time, while interfaces maintain an illusion of per-interface GC times in ! 798: * rip_send_response(). ! 799: * ! 800: * Keeping incoming routes and the selected outgoing route are two independent ! 801: * functions, therefore after garbage collection some entries now considered ! 802: * invalid (RIP_ENTRY_DUMMY) still may have non-empty list of incoming routes, ! 803: * while some valid entries (representing an outgoing route) may have that list ! 804: * empty. ! 805: * ! 806: * The main timer is not scheduled periodically but it uses the time of the ! 807: * current next event and the minimal interval of any possible event to compute ! 808: * the time of the next run. ! 809: */ ! 810: static void ! 811: rip_timer(timer *t) ! 812: { ! 813: struct rip_proto *p = t->data; ! 814: struct rip_config *cf = (void *) (p->p.cf); ! 815: struct rip_iface *ifa; ! 816: struct rip_neighbor *n, *nn; ! 817: struct fib_iterator fit; ! 818: btime now_ = current_time(); ! 819: btime next = now_ + MIN(cf->min_timeout_time, cf->max_garbage_time); ! 820: btime expires = 0; ! 821: ! 822: TRACE(D_EVENTS, "Main timer fired"); ! 823: ! 824: FIB_ITERATE_INIT(&fit, &p->rtable); ! 825: ! 826: loop: ! 827: FIB_ITERATE_START(&p->rtable, &fit, struct rip_entry, en) ! 828: { ! 829: struct rip_rte *rt, **rp; ! 830: int changed = 0; ! 831: ! 832: /* Checking received routes for timeout and for dead neighbors */ ! 833: for (rp = &en->routes; rt = *rp; /* rp = &rt->next */) ! 834: { ! 835: if (!rip_valid_rte(rt) || (rt->expires <= now_)) ! 836: { ! 837: rip_remove_rte(p, rp); ! 838: changed = 1; ! 839: continue; ! 840: } ! 841: ! 842: next = MIN(next, rt->expires); ! 843: rp = &rt->next; ! 844: } ! 845: ! 846: /* Propagating eventual change */ ! 847: if (changed || p->rt_reload) ! 848: { ! 849: /* ! 850: * We have to restart the iteration because there may be a cascade of ! 851: * synchronous events rip_announce_rte() -> nest table change -> ! 852: * rip_rt_notify() -> p->rtable change, invalidating hidden variables. ! 853: */ ! 854: ! 855: FIB_ITERATE_PUT_NEXT(&fit, &p->rtable); ! 856: rip_announce_rte(p, en); ! 857: goto loop; ! 858: } ! 859: ! 860: /* Checking stale entries for garbage collection timeout */ ! 861: if (en->valid == RIP_ENTRY_STALE) ! 862: { ! 863: expires = en->changed + cf->max_garbage_time; ! 864: ! 865: if (expires <= now_) ! 866: { ! 867: // TRACE(D_EVENTS, "entry is too old: %N", en->n.addr); ! 868: en->valid = 0; ! 869: } ! 870: else ! 871: next = MIN(next, expires); ! 872: } ! 873: ! 874: /* Remove empty nodes */ ! 875: if (!en->valid && !en->routes) ! 876: { ! 877: FIB_ITERATE_PUT(&fit); ! 878: fib_delete(&p->rtable, en); ! 879: goto loop; ! 880: } ! 881: } ! 882: FIB_ITERATE_END; ! 883: ! 884: p->rt_reload = 0; ! 885: ! 886: /* Handling neighbor expiration */ ! 887: WALK_LIST(ifa, p->iface_list) ! 888: WALK_LIST_DELSAFE(n, nn, ifa->neigh_list) ! 889: if (n->last_seen) ! 890: { ! 891: expires = n->last_seen + n->ifa->cf->timeout_time; ! 892: ! 893: if (expires <= now_) ! 894: rip_remove_neighbor(p, n); ! 895: else ! 896: next = MIN(next, expires); ! 897: } ! 898: ! 899: tm_start(p->timer, MAX(next - now_, 100 MS)); ! 900: } ! 901: ! 902: static inline void ! 903: rip_kick_timer(struct rip_proto *p) ! 904: { ! 905: if (p->timer->expires > (current_time() + 100 MS)) ! 906: tm_start(p->timer, 100 MS); ! 907: } ! 908: ! 909: /** ! 910: * rip_iface_timer - RIP interface timer hook ! 911: * @t: timer ! 912: * ! 913: * RIP interface timers are responsible for scheduling both regular and ! 914: * triggered updates. Fixed, delay-independent period is used for regular ! 915: * updates, while minimal separating interval is enforced for triggered updates. ! 916: * The function also ensures that a new update is not started when the old one ! 917: * is still running. ! 918: */ ! 919: static void ! 920: rip_iface_timer(timer *t) ! 921: { ! 922: struct rip_iface *ifa = t->data; ! 923: struct rip_proto *p = ifa->rip; ! 924: btime now_ = current_time(); ! 925: btime period = ifa->cf->update_time; ! 926: ! 927: if (ifa->cf->passive) ! 928: return; ! 929: ! 930: TRACE(D_EVENTS, "Interface timer fired for %s", ifa->iface->name); ! 931: ! 932: if (ifa->tx_active) ! 933: { ! 934: if (now_ < (ifa->next_regular + period)) ! 935: { tm_start(ifa->timer, 100 MS); return; } ! 936: ! 937: /* We are too late, reset is done by rip_send_table() */ ! 938: log(L_WARN "%s: Too slow update on %s, resetting", p->p.name, ifa->iface->name); ! 939: } ! 940: ! 941: if (now_ >= ifa->next_regular) ! 942: { ! 943: /* Send regular update, set timer for next period (or following one if necessay) */ ! 944: TRACE(D_EVENTS, "Sending regular updates for %s", ifa->iface->name); ! 945: rip_send_table(p, ifa, ifa->addr, 0); ! 946: ifa->next_regular += period * (1 + ((now_ - ifa->next_regular) / period)); ! 947: ifa->want_triggered = 0; ! 948: p->triggered = 0; ! 949: } ! 950: else if (ifa->want_triggered && (now_ >= ifa->next_triggered)) ! 951: { ! 952: /* Send triggered update, enforce interval between triggered updates */ ! 953: TRACE(D_EVENTS, "Sending triggered updates for %s", ifa->iface->name); ! 954: rip_send_table(p, ifa, ifa->addr, ifa->want_triggered); ! 955: ifa->next_triggered = now_ + MIN(5 S, period / 2); ! 956: ifa->want_triggered = 0; ! 957: p->triggered = 0; ! 958: } ! 959: ! 960: tm_start(ifa->timer, ifa->want_triggered ? (1 S) : (ifa->next_regular - now_)); ! 961: } ! 962: ! 963: static inline void ! 964: rip_iface_kick_timer(struct rip_iface *ifa) ! 965: { ! 966: if (ifa->timer->expires > (current_time() + 100 MS)) ! 967: tm_start(ifa->timer, 100 MS); ! 968: } ! 969: ! 970: static void ! 971: rip_trigger_update(struct rip_proto *p) ! 972: { ! 973: if (p->triggered) ! 974: return; ! 975: ! 976: struct rip_iface *ifa; ! 977: WALK_LIST(ifa, p->iface_list) ! 978: { ! 979: /* Interface not active */ ! 980: if (! ifa->up) ! 981: continue; ! 982: ! 983: /* Already scheduled */ ! 984: if (ifa->want_triggered) ! 985: continue; ! 986: ! 987: TRACE(D_EVENTS, "Scheduling triggered updates for %s", ifa->iface->name); ! 988: ifa->want_triggered = current_time(); ! 989: rip_iface_kick_timer(ifa); ! 990: } ! 991: ! 992: p->triggered = 1; ! 993: } ! 994: ! 995: ! 996: /* ! 997: * RIP protocol glue ! 998: */ ! 999: ! 1000: static void ! 1001: rip_reload_routes(struct channel *C) ! 1002: { ! 1003: struct rip_proto *p = (struct rip_proto *) C->proto; ! 1004: ! 1005: if (p->rt_reload) ! 1006: return; ! 1007: ! 1008: TRACE(D_EVENTS, "Scheduling route reload"); ! 1009: p->rt_reload = 1; ! 1010: rip_kick_timer(p); ! 1011: } ! 1012: ! 1013: static void ! 1014: rip_make_tmp_attrs(struct rte *rt, struct linpool *pool) ! 1015: { ! 1016: rte_init_tmp_attrs(rt, pool, 2); ! 1017: rte_make_tmp_attr(rt, EA_RIP_METRIC, EAF_TYPE_INT, rt->u.rip.metric); ! 1018: rte_make_tmp_attr(rt, EA_RIP_TAG, EAF_TYPE_INT, rt->u.rip.tag); ! 1019: } ! 1020: ! 1021: static void ! 1022: rip_store_tmp_attrs(struct rte *rt, struct linpool *pool) ! 1023: { ! 1024: rte_init_tmp_attrs(rt, pool, 2); ! 1025: rt->u.rip.metric = rte_store_tmp_attr(rt, EA_RIP_METRIC); ! 1026: rt->u.rip.tag = rte_store_tmp_attr(rt, EA_RIP_TAG); ! 1027: } ! 1028: ! 1029: static int ! 1030: rip_rte_better(struct rte *new, struct rte *old) ! 1031: { ! 1032: return new->u.rip.metric < old->u.rip.metric; ! 1033: } ! 1034: ! 1035: static int ! 1036: rip_rte_same(struct rte *new, struct rte *old) ! 1037: { ! 1038: return ((new->u.rip.metric == old->u.rip.metric) && ! 1039: (new->u.rip.tag == old->u.rip.tag) && ! 1040: (new->u.rip.from == old->u.rip.from)); ! 1041: } ! 1042: ! 1043: ! 1044: static void ! 1045: rip_postconfig(struct proto_config *CF) ! 1046: { ! 1047: // struct rip_config *cf = (void *) CF; ! 1048: ! 1049: /* Define default channel */ ! 1050: if (EMPTY_LIST(CF->channels)) ! 1051: channel_config_new(NULL, net_label[CF->net_type], CF->net_type, CF); ! 1052: } ! 1053: ! 1054: static struct proto * ! 1055: rip_init(struct proto_config *CF) ! 1056: { ! 1057: struct proto *P = proto_new(CF); ! 1058: ! 1059: P->main_channel = proto_add_channel(P, proto_cf_main_channel(CF)); ! 1060: ! 1061: P->if_notify = rip_if_notify; ! 1062: P->rt_notify = rip_rt_notify; ! 1063: P->neigh_notify = rip_neigh_notify; ! 1064: P->reload_routes = rip_reload_routes; ! 1065: P->make_tmp_attrs = rip_make_tmp_attrs; ! 1066: P->store_tmp_attrs = rip_store_tmp_attrs; ! 1067: P->rte_better = rip_rte_better; ! 1068: P->rte_same = rip_rte_same; ! 1069: ! 1070: return P; ! 1071: } ! 1072: ! 1073: static int ! 1074: rip_start(struct proto *P) ! 1075: { ! 1076: struct rip_proto *p = (void *) P; ! 1077: struct rip_config *cf = (void *) (P->cf); ! 1078: ! 1079: init_list(&p->iface_list); ! 1080: fib_init(&p->rtable, P->pool, cf->rip2 ? NET_IP4 : NET_IP6, ! 1081: sizeof(struct rip_entry), OFFSETOF(struct rip_entry, n), 0, NULL); ! 1082: p->rte_slab = sl_new(P->pool, sizeof(struct rip_rte)); ! 1083: p->timer = tm_new_init(P->pool, rip_timer, p, 0, 0); ! 1084: ! 1085: p->rip2 = cf->rip2; ! 1086: p->ecmp = cf->ecmp; ! 1087: p->infinity = cf->infinity; ! 1088: p->triggered = 0; ! 1089: ! 1090: p->log_pkt_tbf = (struct tbf){ .rate = 1, .burst = 5 }; ! 1091: p->log_rte_tbf = (struct tbf){ .rate = 4, .burst = 20 }; ! 1092: ! 1093: tm_start(p->timer, MIN(cf->min_timeout_time, cf->max_garbage_time)); ! 1094: ! 1095: return PS_UP; ! 1096: } ! 1097: ! 1098: static int ! 1099: rip_reconfigure(struct proto *P, struct proto_config *CF) ! 1100: { ! 1101: struct rip_proto *p = (void *) P; ! 1102: struct rip_config *new = (void *) CF; ! 1103: // struct rip_config *old = (void *) (P->cf); ! 1104: ! 1105: if (new->rip2 != p->rip2) ! 1106: return 0; ! 1107: ! 1108: if (new->infinity != p->infinity) ! 1109: return 0; ! 1110: ! 1111: if (!proto_configure_channel(P, &P->main_channel, proto_cf_main_channel(CF))) ! 1112: return 0; ! 1113: ! 1114: TRACE(D_EVENTS, "Reconfiguring"); ! 1115: ! 1116: p->p.cf = CF; ! 1117: p->ecmp = new->ecmp; ! 1118: rip_reconfigure_ifaces(p, new); ! 1119: ! 1120: p->rt_reload = 1; ! 1121: rip_kick_timer(p); ! 1122: ! 1123: return 1; ! 1124: } ! 1125: ! 1126: static void ! 1127: rip_get_route_info(rte *rte, byte *buf) ! 1128: { ! 1129: buf += bsprintf(buf, " (%d/%d)", rte->pref, rte->u.rip.metric); ! 1130: ! 1131: if (rte->u.rip.tag) ! 1132: bsprintf(buf, " [%04x]", rte->u.rip.tag); ! 1133: } ! 1134: ! 1135: static int ! 1136: rip_get_attr(eattr *a, byte *buf, int buflen UNUSED) ! 1137: { ! 1138: switch (a->id) ! 1139: { ! 1140: case EA_RIP_METRIC: ! 1141: bsprintf(buf, "metric: %d", a->u.data); ! 1142: return GA_FULL; ! 1143: ! 1144: case EA_RIP_TAG: ! 1145: bsprintf(buf, "tag: %04x", a->u.data); ! 1146: return GA_FULL; ! 1147: ! 1148: default: ! 1149: return GA_UNKNOWN; ! 1150: } ! 1151: } ! 1152: ! 1153: void ! 1154: rip_show_interfaces(struct proto *P, char *iff) ! 1155: { ! 1156: struct rip_proto *p = (void *) P; ! 1157: struct rip_iface *ifa = NULL; ! 1158: struct rip_neighbor *n = NULL; ! 1159: ! 1160: if (p->p.proto_state != PS_UP) ! 1161: { ! 1162: cli_msg(-1021, "%s: is not up", p->p.name); ! 1163: cli_msg(0, ""); ! 1164: return; ! 1165: } ! 1166: ! 1167: cli_msg(-1021, "%s:", p->p.name); ! 1168: cli_msg(-1021, "%-10s %-6s %6s %6s %7s", ! 1169: "Interface", "State", "Metric", "Nbrs", "Timer"); ! 1170: ! 1171: WALK_LIST(ifa, p->iface_list) ! 1172: { ! 1173: if (iff && !patmatch(iff, ifa->iface->name)) ! 1174: continue; ! 1175: ! 1176: int nbrs = 0; ! 1177: WALK_LIST(n, ifa->neigh_list) ! 1178: if (n->last_seen) ! 1179: nbrs++; ! 1180: ! 1181: btime now_ = current_time(); ! 1182: btime timer = (ifa->next_regular > now_) ? (ifa->next_regular - now_) : 0; ! 1183: cli_msg(-1021, "%-10s %-6s %6u %6u %7t", ! 1184: ifa->iface->name, (ifa->up ? "Up" : "Down"), ifa->cf->metric, nbrs, timer); ! 1185: } ! 1186: ! 1187: cli_msg(0, ""); ! 1188: } ! 1189: ! 1190: void ! 1191: rip_show_neighbors(struct proto *P, char *iff) ! 1192: { ! 1193: struct rip_proto *p = (void *) P; ! 1194: struct rip_iface *ifa = NULL; ! 1195: struct rip_neighbor *n = NULL; ! 1196: ! 1197: if (p->p.proto_state != PS_UP) ! 1198: { ! 1199: cli_msg(-1022, "%s: is not up", p->p.name); ! 1200: cli_msg(0, ""); ! 1201: return; ! 1202: } ! 1203: ! 1204: cli_msg(-1022, "%s:", p->p.name); ! 1205: cli_msg(-1022, "%-25s %-10s %6s %6s %7s", ! 1206: "IP address", "Interface", "Metric", "Routes", "Seen"); ! 1207: ! 1208: WALK_LIST(ifa, p->iface_list) ! 1209: { ! 1210: if (iff && !patmatch(iff, ifa->iface->name)) ! 1211: continue; ! 1212: ! 1213: WALK_LIST(n, ifa->neigh_list) ! 1214: { ! 1215: if (!n->last_seen) ! 1216: continue; ! 1217: ! 1218: btime timer = current_time() - n->last_seen; ! 1219: cli_msg(-1022, "%-25I %-10s %6u %6u %7t", ! 1220: n->nbr->addr, ifa->iface->name, ifa->cf->metric, n->uc, timer); ! 1221: } ! 1222: } ! 1223: ! 1224: cli_msg(0, ""); ! 1225: } ! 1226: ! 1227: static void ! 1228: rip_dump(struct proto *P) ! 1229: { ! 1230: struct rip_proto *p = (struct rip_proto *) P; ! 1231: struct rip_iface *ifa; ! 1232: int i; ! 1233: ! 1234: i = 0; ! 1235: FIB_WALK(&p->rtable, struct rip_entry, en) ! 1236: { ! 1237: debug("RIP: entry #%d: %N via %I dev %s valid %d metric %d age %t\n", ! 1238: i++, en->n.addr, en->next_hop, en->iface->name, ! 1239: en->valid, en->metric, current_time() - en->changed); ! 1240: } ! 1241: FIB_WALK_END; ! 1242: ! 1243: i = 0; ! 1244: WALK_LIST(ifa, p->iface_list) ! 1245: { ! 1246: debug("RIP: interface #%d: %s, %I, up = %d, busy = %d\n", ! 1247: i++, ifa->iface->name, ifa->sk ? ifa->sk->daddr : IPA_NONE, ! 1248: ifa->up, ifa->tx_active); ! 1249: } ! 1250: } ! 1251: ! 1252: ! 1253: struct protocol proto_rip = { ! 1254: .name = "RIP", ! 1255: .template = "rip%d", ! 1256: .class = PROTOCOL_RIP, ! 1257: .preference = DEF_PREF_RIP, ! 1258: .channel_mask = NB_IP, ! 1259: .proto_size = sizeof(struct rip_proto), ! 1260: .config_size = sizeof(struct rip_config), ! 1261: .postconfig = rip_postconfig, ! 1262: .init = rip_init, ! 1263: .dump = rip_dump, ! 1264: .start = rip_start, ! 1265: .reconfigure = rip_reconfigure, ! 1266: .get_route_info = rip_get_route_info, ! 1267: .get_attr = rip_get_attr ! 1268: };