]> git.ipfire.org Git - thirdparty/bird.git/blame - proto/rip/rip.c
RIP: Fix handling of passive mode for demand circuit interfaces
[thirdparty/bird.git] / proto / rip / rip.c
CommitLineData
a103373f 1/*
8465dccb 2 * BIRD -- Routing Information Protocol (RIP)
a103373f 3 *
8465dccb
OZ
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.
a103373f
PM
8 *
9 * Can be freely distributed and used under the terms of the GNU GPL.
10 */
11
2337ade7 12/**
8465dccb
OZ
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().
2337ade7 37 *
8465dccb
OZ
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.
4c5f93d7 43 *
8465dccb
OZ
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.
58f7d004 54 *
8465dccb
OZ
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.
2337ade7 62 *
8465dccb
OZ
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.
2337ade7 70 *
8465dccb
OZ
71 * Supported standards:
72 * - RFC 1058 - RIPv1
73 * - RFC 2453 - RIPv2
74 * - RFC 2080 - RIPng
75 * - RFC 4822 - RIP cryptographic authentication
2337ade7
PM
76 */
77
8465dccb 78#include <stdlib.h>
a103373f
PM
79#include "rip.h"
80
293e313e 81
8465dccb
OZ
82static inline void rip_lock_neighbor(struct rip_neighbor *n);
83static inline void rip_unlock_neighbor(struct rip_neighbor *n);
84static inline int rip_iface_link_up(struct rip_iface *ifa);
85static inline void rip_kick_timer(struct rip_proto *p);
86static inline void rip_iface_kick_timer(struct rip_iface *ifa);
87static void rip_iface_timer(timer *timer);
88static void rip_trigger_update(struct rip_proto *p);
cb822c07 89
b94bbe00 90
4c5f93d7 91/*
8465dccb 92 * RIP routes
279f4c7b
PM
93 */
94
8465dccb
OZ
95static struct rip_rte *
96rip_add_rte(struct rip_proto *p, struct rip_rte **rp, struct rip_rte *src)
7e61cac3 97{
8465dccb 98 struct rip_rte *rt = sl_alloc(p->rte_slab);
d0031c5e 99
8465dccb
OZ
100 memcpy(rt, src, sizeof(struct rip_rte));
101 rt->next = *rp;
102 *rp = rt;
18b4d6bf 103
8465dccb 104 rip_lock_neighbor(rt->from);
7f704c06 105
8465dccb 106 return rt;
7e61cac3
PM
107}
108
8465dccb
OZ
109static inline void
110rip_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
120static 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
123static 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().
b093c328 133 */
279f4c7b 134static void
8465dccb 135rip_announce_rte(struct rip_proto *p, struct rip_entry *en)
279f4c7b 136{
8465dccb
OZ
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 */
8465dccb
OZ
146 rta a0 = {
147 .src = p->p.main_source,
148 .source = RTS_RIP,
149 .scope = SCOPE_UNIVERSE,
62e64905 150 .dest = RTD_UNICAST,
8465dccb 151 };
6996f459 152
8465dccb
OZ
153 u8 rt_metric = rt->metric;
154 u16 rt_tag = rt->tag;
8465dccb 155
62e64905 156 if (p->ecmp)
8465dccb
OZ
157 {
158 /* ECMP route */
62e64905 159 struct nexthop *nhs = NULL;
8465dccb 160 int num = 0;
1b16029c 161
8465dccb
OZ
162 for (rt = en->routes; rt && (num < p->ecmp); rt = rt->next)
163 {
164 if (!rip_valid_rte(rt))
165 continue;
166
62e64905 167 struct nexthop *nh = allocz(sizeof(struct nexthop));
4e276a89 168
8465dccb 169 nh->gw = rt->next_hop;
3343088a 170 nh->iface = rt->from->ifa->iface;
8465dccb 171 nh->weight = rt->from->ifa->cf->ecmp_weight;
4e276a89 172
62e64905 173 nexthop_insert(&nhs, nh);
8465dccb
OZ
174 num++;
175
176 if (rt->tag != rt_tag)
177 rt_tag = 0;
178 }
62e64905
OZ
179
180 a0.nh = *nhs;
bd215f8b 181 }
772f4899 182 else
8465dccb
OZ
183 {
184 /* Unipath route */
62e64905 185 a0.from = rt->from->nbr->addr;
4e276a89 186 a0.nh.gw = rt->next_hop;
3343088a 187 a0.nh.iface = rt->from->ifa->iface;
8465dccb 188 }
279f4c7b 189
8465dccb
OZ
190 rta *a = rta_lookup(&a0);
191 rte *e = rte_get_temp(a);
279f4c7b 192
4e276a89 193 e->u.rip.from = a0.nh.iface;
8465dccb
OZ
194 e->u.rip.metric = rt_metric;
195 e->u.rip.tag = rt_tag;
875cc073 196 e->pflags = EA_ID_FLAG(EA_RIP_METRIC) | EA_ID_FLAG(EA_RIP_TAG);
8465dccb 197
2003a184 198 rte_update(&p->p, en->n.addr, e);
c3e9b2ab 199 }
28323d9d 200 else
8465dccb
OZ
201 {
202 /* Withdraw */
2003a184 203 rte_update(&p->p, en->n.addr, NULL);
8465dccb 204 }
279f4c7b
PM
205}
206
8465dccb
OZ
207/**
208 * rip_update_rte - enter a route update to RIP routing table
209 * @p: RIP instance
fe9f1a6d 210 * @addr: network address
8465dccb
OZ
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 */
219void
fe9f1a6d 220rip_update_rte(struct rip_proto *p, net_addr *n, struct rip_rte *new)
2d9290e9 221{
fe9f1a6d 222 struct rip_entry *en = fib_get(&p->rtable, n);
8465dccb
OZ
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 }
02933ddb 240
8465dccb
OZ
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);
2d9290e9
PM
257}
258
8465dccb
OZ
259/**
260 * rip_withdraw_rte - enter a route withdraw to RIP routing table
261 * @p: RIP instance
fe9f1a6d 262 * @addr: network address
8465dccb 263 * @from: a &rip_neighbor propagating the withdraw
b093c328 264 *
8465dccb
OZ
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().
279f4c7b 268 */
8465dccb 269void
fe9f1a6d 270rip_withdraw_rte(struct rip_proto *p, net_addr *n, struct rip_neighbor *from)
8465dccb 271{
fe9f1a6d 272 struct rip_entry *en = fib_find(&p->rtable, n);
8465dccb 273 struct rip_rte *rt, **rp;
279f4c7b 274
8465dccb
OZ
275 if (!en)
276 return;
094d2bdb 277
8465dccb
OZ
278 /* Find the old route */
279 for (rp = &en->routes; rt = *rp; rp = &rt->next)
280 if (rt->from == from)
281 break;
14758d87 282
8465dccb
OZ
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);
14758d87
PM
292}
293
4c5f93d7 294/*
8465dccb
OZ
295 * rip_rt_notify - core tells us about new route, so store
296 * it into our data structures.
b093c328 297 */
21580e30 298static void
4bdf1881 299rip_rt_notify(struct proto *P, struct channel *ch UNUSED, struct network *net, struct rte *new,
13c0be19 300 struct rte *old UNUSED)
21580e30 301{
8465dccb
OZ
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 */
13c0be19
JMM
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);
8465dccb
OZ
311
312 if (rt_metric > p->infinity)
313 {
fe9f1a6d
OZ
314 log(L_WARN "%s: Invalid rip_metric value %u for route %N",
315 p->p.name, rt_metric, net->n.addr);
8465dccb
OZ
316 rt_metric = p->infinity;
317 }
318
319 if (rt_tag > 0xffff)
320 {
fe9f1a6d
OZ
321 log(L_WARN "%s: Invalid rip_tag value %u for route %N",
322 p->p.name, rt_tag, net->n.addr);
8465dccb
OZ
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
fe9f1a6d 334 en = fib_get(&p->rtable, net->n.addr);
8465dccb
OZ
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;
4e276a89
JMM
342 en->iface = new->attrs->nh.iface;
343 en->next_hop = new->attrs->nh.gw;
491cd43b 344 }
8465dccb
OZ
345 else
346 {
347 /* Withdraw */
fe9f1a6d 348 en = fib_find(&p->rtable, net->n.addr);
feb6abe0 349
8465dccb
OZ
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;
02933ddb 361 }
8465dccb
OZ
362
363 /* Activate triggered updates */
364 if (en->metric != old_metric)
365 {
92cc1e74 366 en->changed = current_time();
8465dccb 367 rip_trigger_update(p);
29df5739 368 }
21580e30 369}
a103373f 370
22c3cf95
OZ
371void
372rip_flush_table(struct rip_proto *p, struct rip_neighbor *n)
373{
374 btime expires = current_time() + n->ifa->cf->timeout_time;
375
376 FIB_WALK(&p->rtable, struct rip_entry, en)
377 {
378 for (struct rip_rte *e = en->routes; e; e = e->next)
379 if ((e->from == n) && (e->expires == TIME_INFINITY))
380 e->expires = expires;
381 }
382 FIB_WALK_END;
383}
384
8465dccb 385
4c5f93d7 386/*
8465dccb 387 * RIP neighbors
b093c328 388 */
8465dccb
OZ
389
390struct rip_neighbor *
391rip_get_neighbor(struct rip_proto *p, ip_addr *a, struct rip_iface *ifa)
392{
586c1800 393 neighbor *nbr = neigh_find(&p->p, *a, ifa->iface, 0);
8465dccb
OZ
394
395 if (!nbr || (nbr->scope == SCOPE_HOST) || !rip_iface_link_up(ifa))
396 return NULL;
397
398 if (nbr->data)
399 return nbr->data;
400
401 TRACE(D_EVENTS, "New neighbor %I on %s", *a, ifa->iface->name);
402
403 struct rip_neighbor *n = mb_allocz(p->p.pool, sizeof(struct rip_neighbor));
404 n->ifa = ifa;
405 n->nbr = nbr;
406 nbr->data = n;
407 n->csn = nbr->aux;
408
409 add_tail(&ifa->neigh_list, NODE n);
410
411 return n;
412}
413
a103373f 414static void
8465dccb 415rip_remove_neighbor(struct rip_proto *p, struct rip_neighbor *n)
a103373f 416{
8465dccb
OZ
417 neighbor *nbr = n->nbr;
418
3343088a 419 TRACE(D_EVENTS, "Removing neighbor %I on %s", nbr->addr, nbr->ifreq->name);
a103373f 420
8465dccb
OZ
421 rem_node(NODE n);
422 n->ifa = NULL;
423 n->nbr = NULL;
424 nbr->data = NULL;
425 nbr->aux = n->csn;
426
427 rfree(n->bfd_req);
428 n->bfd_req = NULL;
429 n->last_seen = 0;
430
431 if (!n->uc)
432 mb_free(n);
433
434 /* Related routes are removed in rip_timer() */
435 rip_kick_timer(p);
a103373f 436}
a103373f 437
8465dccb
OZ
438static inline void
439rip_lock_neighbor(struct rip_neighbor *n)
440{
441 n->uc++;
442}
279f4c7b 443
8465dccb
OZ
444static inline void
445rip_unlock_neighbor(struct rip_neighbor *n)
a103373f 446{
8465dccb
OZ
447 n->uc--;
448
449 if (!n->nbr && !n->uc)
450 mb_free(n);
451}
452
453static void
454rip_neigh_notify(struct neighbor *nbr)
455{
456 struct rip_proto *p = (struct rip_proto *) nbr->proto;
457 struct rip_neighbor *n = nbr->data;
458
459 if (!n)
460 return;
461
462 /*
463 * We assume that rip_neigh_notify() is called before rip_if_notify() for
464 * IF_CHANGE_DOWN and therefore n->ifa is still valid. We have no such
465 * ordering assumption for IF_CHANGE_LINK, so we test link state of the
466 * underlying iface instead of just rip_iface state.
467 */
468 if ((nbr->scope <= 0) || !rip_iface_link_up(n->ifa))
469 rip_remove_neighbor(p, n);
470}
471
472static void
473rip_bfd_notify(struct bfd_request *req)
474{
475 struct rip_neighbor *n = req->data;
476 struct rip_proto *p = n->ifa->rip;
a103373f 477
8465dccb
OZ
478 if (req->down)
479 {
480 TRACE(D_EVENTS, "BFD session down for nbr %I on %s",
481 n->nbr->addr, n->ifa->iface->name);
482 rip_remove_neighbor(p, n);
a103373f 483 }
8465dccb
OZ
484}
485
486void
487rip_update_bfd(struct rip_proto *p, struct rip_neighbor *n)
488{
489 int use_bfd = n->ifa->cf->bfd && n->last_seen;
a103373f 490
8465dccb
OZ
491 if (use_bfd && !n->bfd_req)
492 {
493 /*
494 * For RIPv2, use the same address as rip_open_socket(). For RIPng, neighbor
495 * should contain an address from the same prefix, thus also link-local. It
496 * may cause problems if two link-local addresses are assigned to one iface.
497 */
498 ip_addr saddr = rip_is_v2(p) ? n->ifa->sk->saddr : n->nbr->ifa->ip;
499 n->bfd_req = bfd_request_session(p->p.pool, n->nbr->addr, saddr,
cf7ff995
OZ
500 n->nbr->iface, p->p.vrf,
501 rip_bfd_notify, n);
a103373f
PM
502 }
503
8465dccb
OZ
504 if (!use_bfd && n->bfd_req)
505 {
506 rfree(n->bfd_req);
507 n->bfd_req = NULL;
508 }
a103373f
PM
509}
510
8465dccb 511
4c5f93d7 512/*
8465dccb 513 * RIP interfaces
b093c328 514 */
8465dccb
OZ
515
516static void
517rip_iface_start(struct rip_iface *ifa)
a103373f 518{
8465dccb 519 struct rip_proto *p = ifa->rip;
a103373f 520
8465dccb 521 TRACE(D_EVENTS, "Starting interface %s", ifa->iface->name);
4a020137 522
22c3cf95
OZ
523 if (! ifa->cf->demand_circuit)
524 {
525 ifa->next_regular = current_time() + (random() % ifa->cf->update_time) + 100 MS;
526 tm_set(ifa->timer, ifa->next_regular);
527 }
528 else
529 {
530 ifa->next_regular = TIME_INFINITY;
531 }
532
8465dccb 533 ifa->up = 1;
f7615037 534
5fc84071
OZ
535 if (ifa->cf->passive)
536 return;
537
22c3cf95
OZ
538 rip_send_request(p, ifa);
539 rip_send_table(p, ifa, ifa->addr, 0);
8465dccb 540}
f7615037 541
8465dccb
OZ
542static void
543rip_iface_stop(struct rip_iface *ifa)
544{
545 struct rip_proto *p = ifa->rip;
546 struct rip_neighbor *n;
70e212f9 547
8465dccb 548 TRACE(D_EVENTS, "Stopping interface %s", ifa->iface->name);
70e212f9 549
8465dccb 550 rip_reset_tx_session(p, ifa);
a103373f 551
22c3cf95
OZ
552 ifa->next_regular = 0;
553 ifa->next_triggered = 0;
554 ifa->want_triggered = 0;
555
d516c68a
OZ
556 if (ifa->tx_pending)
557 ifa->tx_seqnum++;
558
22c3cf95
OZ
559 ifa->tx_pending = 0;
560 ifa->req_pending = 0;
561
5fc84071 562 if (ifa->cf->demand_circuit && !ifa->cf->passive)
d516c68a
OZ
563 rip_send_flush(p, ifa);
564
8465dccb
OZ
565 WALK_LIST_FIRST(n, ifa->neigh_list)
566 rip_remove_neighbor(p, n);
0bff946c 567
a6f79ca5 568 tm_stop(ifa->timer);
22c3cf95 569 tm_stop(ifa->rxmt_timer);
8465dccb 570 ifa->up = 0;
a103373f
PM
571}
572
8465dccb
OZ
573static inline int
574rip_iface_link_up(struct rip_iface *ifa)
575{
576 return !ifa->cf->check_link || (ifa->iface->flags & IF_LINK_UP);
577}
279f4c7b 578
a103373f 579static void
8465dccb 580rip_iface_update_state(struct rip_iface *ifa)
a103373f 581{
8465dccb 582 int up = ifa->sk && rip_iface_link_up(ifa);
a103373f 583
8465dccb
OZ
584 if (up == ifa->up)
585 return;
586
587 if (up)
588 rip_iface_start(ifa);
589 else
590 rip_iface_stop(ifa);
591}
4c5f93d7 592
a103373f 593static void
8465dccb 594rip_iface_update_buffers(struct rip_iface *ifa)
a103373f 595{
8465dccb
OZ
596 if (!ifa->sk)
597 return;
a103373f 598
8465dccb
OZ
599 uint rbsize = ifa->cf->rx_buffer ?: ifa->iface->mtu;
600 uint tbsize = ifa->cf->tx_length ?: ifa->iface->mtu;
601 rbsize = MAX(rbsize, tbsize);
de41dcd1 602
8465dccb
OZ
603 sk_set_rbsize(ifa->sk, rbsize);
604 sk_set_tbsize(ifa->sk, tbsize);
de41dcd1 605
8465dccb
OZ
606 uint headers = (rip_is_v2(ifa->rip) ? IP4_HEADER_LENGTH : IP6_HEADER_LENGTH) + UDP_HEADER_LENGTH;
607 ifa->tx_plen = tbsize - headers;
feb6abe0 608
8465dccb 609 if (ifa->cf->auth_type == RIP_AUTH_CRYPTO)
390601f0 610 ifa->tx_plen -= RIP_AUTH_TAIL_LENGTH + max_mac_length(ifa->cf->passwords);
8465dccb 611}
3918b1b0 612
8465dccb
OZ
613static inline void
614rip_iface_update_bfd(struct rip_iface *ifa)
615{
616 struct rip_proto *p = ifa->rip;
617 struct rip_neighbor *n;
618
619 WALK_LIST(n, ifa->neigh_list)
620 rip_update_bfd(p, n);
621}
622
623
624static void
625rip_iface_locked(struct object_lock *lock)
626{
627 struct rip_iface *ifa = lock->data;
628 struct rip_proto *p = ifa->rip;
279f4c7b 629
8465dccb 630 if (!rip_open_socket(ifa))
feb6abe0 631 {
8465dccb
OZ
632 log(L_ERR "%s: Cannot open socket for %s", p->p.name, ifa->iface->name);
633 return;
634 }
a9c38203 635
8465dccb
OZ
636 rip_iface_update_buffers(ifa);
637 rip_iface_update_state(ifa);
638}
a9c38203 639
279f4c7b 640
8465dccb
OZ
641static struct rip_iface *
642rip_find_iface(struct rip_proto *p, struct iface *what)
643{
644 struct rip_iface *ifa;
feb6abe0 645
8465dccb
OZ
646 WALK_LIST(ifa, p->iface_list)
647 if (ifa->iface == what)
648 return ifa;
1d941de4 649
8465dccb 650 return NULL;
a103373f
PM
651}
652
8465dccb
OZ
653static void
654rip_add_iface(struct rip_proto *p, struct iface *iface, struct rip_iface_config *ic)
a103373f 655{
8465dccb
OZ
656 struct rip_iface *ifa;
657
658 TRACE(D_EVENTS, "Adding interface %s", iface->name);
659
660 ifa = mb_allocz(p->p.pool, sizeof(struct rip_iface));
661 ifa->rip = p;
662 ifa->iface = iface;
663 ifa->cf = ic;
664
665 if (ipa_nonzero(ic->address))
666 ifa->addr = ic->address;
667 else if (ic->mode == RIP_IM_MULTICAST)
668 ifa->addr = rip_is_v2(p) ? IP4_RIP_ROUTERS : IP6_RIP_ROUTERS;
669 else /* Broadcast */
153f02da
OZ
670 ifa->addr = iface->addr4->brd;
671 /*
672 * The above is just a workaround for BSD as it can't send broadcasts
673 * to 255.255.255.255. BSD systems need the network broadcast address instead.
674 *
675 * TODO: move this to sysdep code
676 */
8465dccb
OZ
677
678 init_list(&ifa->neigh_list);
679
680 add_tail(&p->iface_list, NODE ifa);
681
a6f79ca5 682 ifa->timer = tm_new_init(p->p.pool, rip_iface_timer, ifa, 0, 0);
22c3cf95 683 ifa->rxmt_timer = tm_new_init(p->p.pool, rip_rxmt_timeout, ifa, 0, 0);
8465dccb
OZ
684
685 struct object_lock *lock = olock_new(p->p.pool);
686 lock->type = OBJLOCK_UDP;
687 lock->port = ic->port;
688 lock->iface = iface;
689 lock->data = ifa;
690 lock->hook = rip_iface_locked;
691 ifa->lock = lock;
692
693 olock_acquire(lock);
a103373f
PM
694}
695
a103373f 696static void
8465dccb 697rip_remove_iface(struct rip_proto *p, struct rip_iface *ifa)
a103373f 698{
8465dccb 699 rip_iface_stop(ifa);
c748cdb9 700
8465dccb
OZ
701 TRACE(D_EVENTS, "Removing interface %s", ifa->iface->name);
702
703 rem_node(NODE ifa);
704
705 rfree(ifa->sk);
706 rfree(ifa->lock);
707 rfree(ifa->timer);
708
709 mb_free(ifa);
710}
711
712static int
713rip_reconfigure_iface(struct rip_proto *p, struct rip_iface *ifa, struct rip_iface_config *new)
714{
715 struct rip_iface_config *old = ifa->cf;
716
717 /* Change of these options would require to reset the iface socket */
718 if ((new->mode != old->mode) ||
719 (new->port != old->port) ||
720 (new->tx_tos != old->tx_tos) ||
721 (new->tx_priority != old->tx_priority) ||
d516c68a
OZ
722 (new->ttl_security != old->ttl_security) ||
723 (new->demand_circuit != old->demand_circuit))
8465dccb
OZ
724 return 0;
725
726 TRACE(D_EVENTS, "Reconfiguring interface %s", ifa->iface->name);
727
728 ifa->cf = new;
729
390601f0
OZ
730 rip_iface_update_buffers(ifa);
731
d516c68a
OZ
732 if ((! ifa->cf->demand_circuit) &&
733 (ifa->next_regular > (current_time() + new->update_time)))
92cc1e74 734 ifa->next_regular = current_time() + (random() % new->update_time) + 100 MS;
8465dccb 735
5fc84071
OZ
736 if (ifa->up && new->demand_circuit && (new->passive != old->passive))
737 {
738 if (new->passive)
739 rip_send_flush(p, ifa);
740 else
741 {
742 rip_send_request(p, ifa);
743 rip_send_table(p, ifa, ifa->addr, 0);
744 }
745 }
746
8465dccb
OZ
747 if (new->check_link != old->check_link)
748 rip_iface_update_state(ifa);
749
750 if (new->bfd != old->bfd)
751 rip_iface_update_bfd(ifa);
752
753 if (ifa->up)
754 rip_iface_kick_timer(ifa);
755
756 return 1;
c3e9b2ab
PM
757}
758
dff1f579 759static void
8465dccb 760rip_reconfigure_ifaces(struct rip_proto *p, struct rip_config *cf)
dff1f579 761{
8465dccb
OZ
762 struct iface *iface;
763
764 WALK_LIST(iface, iface_list)
765 {
153f02da
OZ
766 if (!(iface->flags & IF_UP))
767 continue;
768
769 /* Ignore ifaces without appropriate address */
770 if (rip_is_v2(p) ? !iface->addr4 : !iface->llv6)
8465dccb
OZ
771 continue;
772
773 struct rip_iface *ifa = rip_find_iface(p, iface);
774 struct rip_iface_config *ic = (void *) iface_patt_find(&cf->patt_list, iface, NULL);
ce1da96e 775
8465dccb
OZ
776 if (ifa && ic)
777 {
778 if (rip_reconfigure_iface(p, ifa, ic))
779 continue;
780
781 /* Hard restart */
782 log(L_INFO "%s: Restarting interface %s", p->p.name, ifa->iface->name);
783 rip_remove_iface(p, ifa);
784 rip_add_iface(p, iface, ic);
785 }
786
787 if (ifa && !ic)
788 rip_remove_iface(p, ifa);
789
790 if (!ifa && ic)
791 rip_add_iface(p, iface, ic);
792 }
dff1f579
PM
793}
794
c3e9b2ab 795static void
8465dccb 796rip_if_notify(struct proto *P, unsigned flags, struct iface *iface)
c3e9b2ab 797{
8465dccb
OZ
798 struct rip_proto *p = (void *) P;
799 struct rip_config *cf = (void *) P->cf;
e2ae0869 800 struct rip_iface *ifa = rip_find_iface(p, iface);
8465dccb
OZ
801
802 if (iface->flags & IF_IGNORE)
803 return;
804
e2ae0869
OZ
805 /* Add, remove or restart interface */
806 if (flags & (IF_CHANGE_UPDOWN | (rip_is_v2(p) ? IF_CHANGE_ADDR4 : IF_CHANGE_LLV6)))
8465dccb 807 {
e2ae0869
OZ
808 if (ifa)
809 rip_remove_iface(p, ifa);
810
811 if (!(iface->flags & IF_UP))
812 return;
8465dccb 813
153f02da
OZ
814 /* Ignore ifaces without appropriate address */
815 if (rip_is_v2(p) ? !iface->addr4 : !iface->llv6)
816 return;
817
e2ae0869 818 struct rip_iface_config *ic = (void *) iface_patt_find(&cf->patt_list, iface, NULL);
8465dccb
OZ
819 if (ic)
820 rip_add_iface(p, iface, ic);
821
822 return;
823 }
824
8465dccb
OZ
825 if (!ifa)
826 return;
827
8465dccb
OZ
828 if (flags & IF_CHANGE_MTU)
829 rip_iface_update_buffers(ifa);
830
831 if (flags & IF_CHANGE_LINK)
832 rip_iface_update_state(ifa);
c3e9b2ab
PM
833}
834
8465dccb
OZ
835
836/*
837 * RIP timer events
838 */
839
6c0a7174 840/**
8465dccb
OZ
841 * rip_timer - RIP main timer hook
842 * @t: timer
4c5f93d7 843 *
8465dccb
OZ
844 * The RIP main timer is responsible for routing table maintenance. Invalid or
845 * expired routes (&rip_rte) are removed and garbage collection of stale routing
846 * table entries (&rip_entry) is done. Changes are propagated to core tables,
847 * route reload is also done here. Note that garbage collection uses a maximal
848 * GC time, while interfaces maintain an illusion of per-interface GC times in
849 * rip_send_response().
850 *
851 * Keeping incoming routes and the selected outgoing route are two independent
852 * functions, therefore after garbage collection some entries now considered
853 * invalid (RIP_ENTRY_DUMMY) still may have non-empty list of incoming routes,
854 * while some valid entries (representing an outgoing route) may have that list
855 * empty.
856 *
857 * The main timer is not scheduled periodically but it uses the time of the
858 * current next event and the minimal interval of any possible event to compute
859 * the time of the next run.
06fa1453 860 */
8465dccb
OZ
861static void
862rip_timer(timer *t)
c3e9b2ab 863{
8465dccb
OZ
864 struct rip_proto *p = t->data;
865 struct rip_config *cf = (void *) (p->p.cf);
866 struct rip_iface *ifa;
867 struct rip_neighbor *n, *nn;
868 struct fib_iterator fit;
92cc1e74
OZ
869 btime now_ = current_time();
870 btime next = now_ + MIN(cf->min_timeout_time, cf->max_garbage_time);
871 btime expires = 0;
8465dccb
OZ
872
873 TRACE(D_EVENTS, "Main timer fired");
874
875 FIB_ITERATE_INIT(&fit, &p->rtable);
876
877 loop:
600998fc 878 FIB_ITERATE_START(&p->rtable, &fit, struct rip_entry, en)
8465dccb 879 {
8465dccb
OZ
880 struct rip_rte *rt, **rp;
881 int changed = 0;
882
883 /* Checking received routes for timeout and for dead neighbors */
884 for (rp = &en->routes; rt = *rp; /* rp = &rt->next */)
e24ddd9b 885 {
92cc1e74 886 if (!rip_valid_rte(rt) || (rt->expires <= now_))
8465dccb
OZ
887 {
888 rip_remove_rte(p, rp);
889 changed = 1;
890 continue;
891 }
06fa1453 892
8465dccb
OZ
893 next = MIN(next, rt->expires);
894 rp = &rt->next;
28323d9d 895 }
06fa1453 896
8465dccb
OZ
897 /* Propagating eventual change */
898 if (changed || p->rt_reload)
899 {
900 /*
901 * We have to restart the iteration because there may be a cascade of
902 * synchronous events rip_announce_rte() -> nest table change ->
903 * rip_rt_notify() -> p->rtable change, invalidating hidden variables.
904 */
905
600998fc 906 FIB_ITERATE_PUT_NEXT(&fit, &p->rtable);
8465dccb
OZ
907 rip_announce_rte(p, en);
908 goto loop;
909 }
f9c799a0 910
8465dccb
OZ
911 /* Checking stale entries for garbage collection timeout */
912 if (en->valid == RIP_ENTRY_STALE)
913 {
914 expires = en->changed + cf->max_garbage_time;
f9c799a0 915
92cc1e74 916 if (expires <= now_)
f9c799a0 917 {
fe9f1a6d 918 // TRACE(D_EVENTS, "entry is too old: %N", en->n.addr);
8465dccb 919 en->valid = 0;
f9c799a0 920 }
8465dccb
OZ
921 else
922 next = MIN(next, expires);
923 }
924
925 /* Remove empty nodes */
926 if (!en->valid && !en->routes)
927 {
600998fc
OZ
928 FIB_ITERATE_PUT(&fit);
929 fib_delete(&p->rtable, en);
8465dccb
OZ
930 goto loop;
931 }
932 }
600998fc 933 FIB_ITERATE_END;
8465dccb
OZ
934
935 p->rt_reload = 0;
936
937 /* Handling neighbor expiration */
938 WALK_LIST(ifa, p->iface_list)
22c3cf95
OZ
939 {
940 /* No expiration for demand circuit ifaces */
941 if (ifa->cf->demand_circuit)
942 continue;
943
8465dccb
OZ
944 WALK_LIST_DELSAFE(n, nn, ifa->neigh_list)
945 if (n->last_seen)
f9c799a0 946 {
8465dccb
OZ
947 expires = n->last_seen + n->ifa->cf->timeout_time;
948
92cc1e74 949 if (expires <= now_)
8465dccb
OZ
950 rip_remove_neighbor(p, n);
951 else
952 next = MIN(next, expires);
f9c799a0 953 }
22c3cf95 954 }
b94bbe00 955
a6f79ca5 956 tm_start(p->timer, MAX(next - now_, 100 MS));
8465dccb
OZ
957}
958
959static inline void
960rip_kick_timer(struct rip_proto *p)
961{
22c3cf95 962 if ((p->timer->expires > (current_time() + 100 MS)))
a6f79ca5 963 tm_start(p->timer, 100 MS);
8465dccb 964}
f9c799a0 965
8465dccb
OZ
966/**
967 * rip_iface_timer - RIP interface timer hook
968 * @t: timer
969 *
970 * RIP interface timers are responsible for scheduling both regular and
971 * triggered updates. Fixed, delay-independent period is used for regular
972 * updates, while minimal separating interval is enforced for triggered updates.
973 * The function also ensures that a new update is not started when the old one
974 * is still running.
975 */
976static void
977rip_iface_timer(timer *t)
978{
979 struct rip_iface *ifa = t->data;
980 struct rip_proto *p = ifa->rip;
92cc1e74
OZ
981 btime now_ = current_time();
982 btime period = ifa->cf->update_time;
8465dccb
OZ
983
984 if (ifa->cf->passive)
985 return;
986
987 TRACE(D_EVENTS, "Interface timer fired for %s", ifa->iface->name);
988
989 if (ifa->tx_active)
990 {
22c3cf95
OZ
991 tm_start(ifa->timer, 100 MS);
992 return;
f9c799a0 993 }
8465dccb 994
92cc1e74 995 if (now_ >= ifa->next_regular)
8465dccb
OZ
996 {
997 /* Send regular update, set timer for next period (or following one if necessay) */
998 TRACE(D_EVENTS, "Sending regular updates for %s", ifa->iface->name);
999 rip_send_table(p, ifa, ifa->addr, 0);
92cc1e74 1000 ifa->next_regular += period * (1 + ((now_ - ifa->next_regular) / period));
8465dccb
OZ
1001 ifa->want_triggered = 0;
1002 p->triggered = 0;
1003 }
92cc1e74 1004 else if (ifa->want_triggered && (now_ >= ifa->next_triggered))
8465dccb
OZ
1005 {
1006 /* Send triggered update, enforce interval between triggered updates */
1007 TRACE(D_EVENTS, "Sending triggered updates for %s", ifa->iface->name);
1008 rip_send_table(p, ifa, ifa->addr, ifa->want_triggered);
92cc1e74 1009 ifa->next_triggered = now_ + MIN(5 S, period / 2);
8465dccb
OZ
1010 ifa->want_triggered = 0;
1011 p->triggered = 0;
1012 }
1013
22c3cf95
OZ
1014 if (ifa->want_triggered && (ifa->next_triggered < ifa->next_regular))
1015 tm_set(ifa->timer, ifa->next_triggered);
1016 else if (ifa->next_regular != TIME_INFINITY)
1017 tm_set(ifa->timer, ifa->next_regular);
a103373f
PM
1018}
1019
22c3cf95 1020
8465dccb
OZ
1021static inline void
1022rip_iface_kick_timer(struct rip_iface *ifa)
ff8ed632 1023{
22c3cf95 1024 if ((! tm_active(ifa->timer)) || (ifa->timer->expires > (current_time() + 100 MS)))
a6f79ca5 1025 tm_start(ifa->timer, 100 MS);
ff8ed632
PM
1026}
1027
279f4c7b 1028static void
8465dccb 1029rip_trigger_update(struct rip_proto *p)
279f4c7b 1030{
8465dccb 1031 if (p->triggered)
9a158361 1032 return;
8465dccb
OZ
1033
1034 struct rip_iface *ifa;
1035 WALK_LIST(ifa, p->iface_list)
1036 {
1037 /* Interface not active */
1038 if (! ifa->up)
1039 continue;
1040
1041 /* Already scheduled */
1042 if (ifa->want_triggered)
1043 continue;
1044
1045 TRACE(D_EVENTS, "Scheduling triggered updates for %s", ifa->iface->name);
92cc1e74 1046 ifa->want_triggered = current_time();
8465dccb 1047 rip_iface_kick_timer(ifa);
22c3cf95 1048 p->triggered = 1;
c3e9b2ab 1049 }
279f4c7b
PM
1050}
1051
8465dccb
OZ
1052
1053/*
1054 * RIP protocol glue
1055 */
1056
f4a60a9b
OZ
1057static void
1058rip_reload_routes(struct channel *C)
8465dccb 1059{
f4a60a9b 1060 struct rip_proto *p = (struct rip_proto *) C->proto;
8465dccb
OZ
1061
1062 if (p->rt_reload)
f4a60a9b 1063 return;
8465dccb
OZ
1064
1065 TRACE(D_EVENTS, "Scheduling route reload");
1066 p->rt_reload = 1;
1067 rip_kick_timer(p);
8465dccb
OZ
1068}
1069
875cc073 1070static void
91c7c741
PM
1071rip_make_tmp_attrs(struct rte *rt, struct linpool *pool)
1072{
875cc073
OZ
1073 rte_init_tmp_attrs(rt, pool, 2);
1074 rte_make_tmp_attr(rt, EA_RIP_METRIC, EAF_TYPE_INT, rt->u.rip.metric);
1075 rte_make_tmp_attr(rt, EA_RIP_TAG, EAF_TYPE_INT, rt->u.rip.tag);
91c7c741
PM
1076}
1077
8465dccb 1078static void
875cc073 1079rip_store_tmp_attrs(struct rte *rt, struct linpool *pool)
91c7c741 1080{
875cc073
OZ
1081 rte_init_tmp_attrs(rt, pool, 2);
1082 rt->u.rip.metric = rte_store_tmp_attr(rt, EA_RIP_METRIC);
1083 rt->u.rip.tag = rte_store_tmp_attr(rt, EA_RIP_TAG);
91c7c741
PM
1084}
1085
8465dccb
OZ
1086static int
1087rip_rte_better(struct rte *new, struct rte *old)
1d941de4 1088{
8465dccb 1089 return new->u.rip.metric < old->u.rip.metric;
1d941de4
PM
1090}
1091
16c2d48d
OF
1092static int
1093rip_rte_same(struct rte *new, struct rte *old)
1094{
8465dccb
OZ
1095 return ((new->u.rip.metric == old->u.rip.metric) &&
1096 (new->u.rip.tag == old->u.rip.tag) &&
1097 (new->u.rip.from == old->u.rip.from));
16c2d48d
OF
1098}
1099
1100
f4a60a9b
OZ
1101static void
1102rip_postconfig(struct proto_config *CF)
1103{
1104 // struct rip_config *cf = (void *) CF;
1105
1106 /* Define default channel */
1107 if (EMPTY_LIST(CF->channels))
72163bd5 1108 channel_config_new(NULL, net_label[CF->net_type], CF->net_type, CF);
f4a60a9b
OZ
1109}
1110
8465dccb 1111static struct proto *
f4a60a9b 1112rip_init(struct proto_config *CF)
8465dccb 1113{
f4a60a9b
OZ
1114 struct proto *P = proto_new(CF);
1115
1116 P->main_channel = proto_add_channel(P, proto_cf_main_channel(CF));
8465dccb 1117
8465dccb
OZ
1118 P->if_notify = rip_if_notify;
1119 P->rt_notify = rip_rt_notify;
1120 P->neigh_notify = rip_neigh_notify;
8465dccb
OZ
1121 P->reload_routes = rip_reload_routes;
1122 P->make_tmp_attrs = rip_make_tmp_attrs;
1123 P->store_tmp_attrs = rip_store_tmp_attrs;
1124 P->rte_better = rip_rte_better;
1125 P->rte_same = rip_rte_same;
1126
1127 return P;
1128}
1129
feb6abe0 1130static int
8465dccb 1131rip_start(struct proto *P)
21580e30 1132{
8465dccb
OZ
1133 struct rip_proto *p = (void *) P;
1134 struct rip_config *cf = (void *) (P->cf);
8d2e3eba 1135
8465dccb 1136 init_list(&p->iface_list);
23c212e7 1137 fib_init(&p->rtable, P->pool, cf->rip2 ? NET_IP4 : NET_IP6,
fe9f1a6d 1138 sizeof(struct rip_entry), OFFSETOF(struct rip_entry, n), 0, NULL);
8465dccb 1139 p->rte_slab = sl_new(P->pool, sizeof(struct rip_rte));
a6f79ca5 1140 p->timer = tm_new_init(P->pool, rip_timer, p, 0, 0);
3918b1b0 1141
23c212e7 1142 p->rip2 = cf->rip2;
8465dccb
OZ
1143 p->ecmp = cf->ecmp;
1144 p->infinity = cf->infinity;
1145 p->triggered = 0;
21580e30 1146
8465dccb
OZ
1147 p->log_pkt_tbf = (struct tbf){ .rate = 1, .burst = 5 };
1148 p->log_rte_tbf = (struct tbf){ .rate = 4, .burst = 20 };
21580e30 1149
a6f79ca5 1150 tm_start(p->timer, MIN(cf->min_timeout_time, cf->max_garbage_time));
21580e30 1151
8465dccb 1152 return PS_UP;
21580e30
PM
1153}
1154
d516c68a
OZ
1155static int
1156rip_shutdown(struct proto *P)
1157{
1158 struct rip_proto *p = (void *) P;
1159
1160 TRACE(D_EVENTS, "Shutdown requested");
1161
1162 struct rip_iface *ifa;
1163 WALK_LIST(ifa, p->iface_list)
1164 rip_iface_stop(ifa);
1165
1166 return PS_DOWN;
1167}
1168
8465dccb 1169static int
f4a60a9b 1170rip_reconfigure(struct proto *P, struct proto_config *CF)
feb6abe0 1171{
8465dccb 1172 struct rip_proto *p = (void *) P;
f4a60a9b 1173 struct rip_config *new = (void *) CF;
8465dccb 1174 // struct rip_config *old = (void *) (P->cf);
feb6abe0 1175
23c212e7
OZ
1176 if (new->rip2 != p->rip2)
1177 return 0;
1178
8465dccb
OZ
1179 if (new->infinity != p->infinity)
1180 return 0;
feb6abe0 1181
f4a60a9b
OZ
1182 if (!proto_configure_channel(P, &P->main_channel, proto_cf_main_channel(CF)))
1183 return 0;
1184
8465dccb
OZ
1185 TRACE(D_EVENTS, "Reconfiguring");
1186
f4a60a9b 1187 p->p.cf = CF;
8465dccb
OZ
1188 p->ecmp = new->ecmp;
1189 rip_reconfigure_ifaces(p, new);
1190
1191 p->rt_reload = 1;
1192 rip_kick_timer(p);
1193
1194 return 1;
c748cdb9 1195}
2d9290e9 1196
8465dccb 1197static void
13c0be19 1198rip_get_route_info(rte *rte, byte *buf)
c748cdb9 1199{
8465dccb
OZ
1200 buf += bsprintf(buf, " (%d/%d)", rte->pref, rte->u.rip.metric);
1201
1202 if (rte->u.rip.tag)
1203 bsprintf(buf, " [%04x]", rte->u.rip.tag);
a103373f
PM
1204}
1205
a769a180 1206static int
258be565 1207rip_get_attr(const eattr *a, byte *buf, int buflen UNUSED)
a769a180 1208{
8465dccb
OZ
1209 switch (a->id)
1210 {
1211 case EA_RIP_METRIC:
1212 bsprintf(buf, "metric: %d", a->u.data);
1213 return GA_FULL;
1214
1215 case EA_RIP_TAG:
1216 bsprintf(buf, "tag: %04x", a->u.data);
1217 return GA_FULL;
1218
1219 default:
1220 return GA_UNKNOWN;
a769a180
PM
1221 }
1222}
1223
8465dccb 1224void
fd9f0c06 1225rip_show_interfaces(struct proto *P, const char *iff)
898fdd85 1226{
8465dccb
OZ
1227 struct rip_proto *p = (void *) P;
1228 struct rip_iface *ifa = NULL;
1229 struct rip_neighbor *n = NULL;
1230
1231 if (p->p.proto_state != PS_UP)
1232 {
1233 cli_msg(-1021, "%s: is not up", p->p.name);
1234 cli_msg(0, "");
1235 return;
1236 }
1237
1238 cli_msg(-1021, "%s:", p->p.name);
92cc1e74 1239 cli_msg(-1021, "%-10s %-6s %6s %6s %7s",
8465dccb
OZ
1240 "Interface", "State", "Metric", "Nbrs", "Timer");
1241
1242 WALK_LIST(ifa, p->iface_list)
1243 {
1244 if (iff && !patmatch(iff, ifa->iface->name))
1245 continue;
1246
1247 int nbrs = 0;
1248 WALK_LIST(n, ifa->neigh_list)
1249 if (n->last_seen)
1250 nbrs++;
1251
92cc1e74 1252 btime now_ = current_time();
22c3cf95
OZ
1253 btime timer = ((ifa->next_regular < TIME_INFINITY) && (ifa->next_regular > now_)) ?
1254 (ifa->next_regular - now_) : 0;
92cc1e74 1255 cli_msg(-1021, "%-10s %-6s %6u %6u %7t",
8465dccb
OZ
1256 ifa->iface->name, (ifa->up ? "Up" : "Down"), ifa->cf->metric, nbrs, timer);
1257 }
1258
1259 cli_msg(0, "");
898fdd85
PM
1260}
1261
8465dccb 1262void
fd9f0c06 1263rip_show_neighbors(struct proto *P, const char *iff)
7f5f44bb 1264{
8465dccb
OZ
1265 struct rip_proto *p = (void *) P;
1266 struct rip_iface *ifa = NULL;
1267 struct rip_neighbor *n = NULL;
7f5f44bb 1268
8465dccb
OZ
1269 if (p->p.proto_state != PS_UP)
1270 {
1271 cli_msg(-1022, "%s: is not up", p->p.name);
1272 cli_msg(0, "");
1273 return;
1274 }
1275
1276 cli_msg(-1022, "%s:", p->p.name);
92cc1e74 1277 cli_msg(-1022, "%-25s %-10s %6s %6s %7s",
8465dccb
OZ
1278 "IP address", "Interface", "Metric", "Routes", "Seen");
1279
1280 WALK_LIST(ifa, p->iface_list)
1281 {
1282 if (iff && !patmatch(iff, ifa->iface->name))
1283 continue;
1284
1285 WALK_LIST(n, ifa->neigh_list)
1286 {
1287 if (!n->last_seen)
1288 continue;
1289
92cc1e74
OZ
1290 btime timer = current_time() - n->last_seen;
1291 cli_msg(-1022, "%-25I %-10s %6u %6u %7t",
8465dccb
OZ
1292 n->nbr->addr, ifa->iface->name, ifa->cf->metric, n->uc, timer);
1293 }
1294 }
1295
1296 cli_msg(0, "");
7f5f44bb
PM
1297}
1298
a7f23f58 1299static void
8465dccb 1300rip_dump(struct proto *P)
a7f23f58 1301{
8465dccb
OZ
1302 struct rip_proto *p = (struct rip_proto *) P;
1303 struct rip_iface *ifa;
1304 int i;
a7f23f58 1305
8465dccb 1306 i = 0;
600998fc 1307 FIB_WALK(&p->rtable, struct rip_entry, en)
8465dccb 1308 {
92cc1e74 1309 debug("RIP: entry #%d: %N via %I dev %s valid %d metric %d age %t\n",
d516c68a 1310 i++, en->n.addr, en->next_hop, en->iface ? en->iface->name : "(null)",
92cc1e74 1311 en->valid, en->metric, current_time() - en->changed);
d516c68a
OZ
1312
1313 for (struct rip_rte *e = en->routes; e; e = e->next)
1314 debug("RIP: via %I metric %d expires %t\n",
1315 e->next_hop, e->metric, e->expires - current_time());
8465dccb
OZ
1316 }
1317 FIB_WALK_END;
a7f23f58 1318
8465dccb
OZ
1319 i = 0;
1320 WALK_LIST(ifa, p->iface_list)
1321 {
1322 debug("RIP: interface #%d: %s, %I, up = %d, busy = %d\n",
1323 i++, ifa->iface->name, ifa->sk ? ifa->sk->daddr : IPA_NONE,
1324 ifa->up, ifa->tx_active);
1325 }
a7f23f58
OZ
1326}
1327
1328
a103373f 1329struct protocol proto_rip = {
4a591d4b
PT
1330 .name = "RIP",
1331 .template = "rip%d",
ee7e2ffd 1332 .class = PROTOCOL_RIP,
4a591d4b 1333 .preference = DEF_PREF_RIP,
f4a60a9b
OZ
1334 .channel_mask = NB_IP,
1335 .proto_size = sizeof(struct rip_proto),
8465dccb 1336 .config_size = sizeof(struct rip_config),
f4a60a9b 1337 .postconfig = rip_postconfig,
4a591d4b
PT
1338 .init = rip_init,
1339 .dump = rip_dump,
1340 .start = rip_start,
d516c68a 1341 .shutdown = rip_shutdown,
4a591d4b 1342 .reconfigure = rip_reconfigure,
2bbc3083
OZ
1343 .get_route_info = rip_get_route_info,
1344 .get_attr = rip_get_attr
a103373f 1345};