]>
Commit | Line | Data |
---|---|---|
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 |
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); | |
cb822c07 | 89 | |
b94bbe00 | 90 | |
4c5f93d7 | 91 | /* |
8465dccb | 92 | * RIP routes |
279f4c7b PM |
93 | */ |
94 | ||
8465dccb OZ |
95 | static struct rip_rte * |
96 | rip_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 |
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(). | |
b093c328 | 133 | */ |
279f4c7b | 134 | static void |
8465dccb | 135 | rip_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 | */ | |
219 | void | |
fe9f1a6d | 220 | rip_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 | 269 | void |
fe9f1a6d | 270 | rip_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 | 298 | static void |
4bdf1881 | 299 | rip_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 |
371 | void |
372 | rip_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 | |
390 | struct rip_neighbor * | |
391 | rip_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 | 414 | static void |
8465dccb | 415 | rip_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 |
438 | static inline void |
439 | rip_lock_neighbor(struct rip_neighbor *n) | |
440 | { | |
441 | n->uc++; | |
442 | } | |
279f4c7b | 443 | |
8465dccb OZ |
444 | static inline void |
445 | rip_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 | ||
453 | static void | |
454 | rip_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 | ||
472 | static void | |
473 | rip_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 | ||
486 | void | |
487 | rip_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 | |
516 | static void | |
517 | rip_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 |
542 | static void |
543 | rip_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 |
573 | static inline int |
574 | rip_iface_link_up(struct rip_iface *ifa) | |
575 | { | |
576 | return !ifa->cf->check_link || (ifa->iface->flags & IF_LINK_UP); | |
577 | } | |
279f4c7b | 578 | |
a103373f | 579 | static void |
8465dccb | 580 | rip_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 | 593 | static void |
8465dccb | 594 | rip_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 |
613 | static inline void |
614 | rip_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 | ||
624 | static void | |
625 | rip_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 |
641 | static struct rip_iface * |
642 | rip_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 |
653 | static void |
654 | rip_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 | 696 | static void |
8465dccb | 697 | rip_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 | ||
712 | static int | |
713 | rip_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 | 759 | static void |
8465dccb | 760 | rip_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 | 795 | static void |
8465dccb | 796 | rip_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 |
861 | static void |
862 | rip_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 | ||
959 | static inline void | |
960 | rip_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 | */ | |
976 | static void | |
977 | rip_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 |
1021 | static inline void |
1022 | rip_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 | 1028 | static void |
8465dccb | 1029 | rip_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 |
1057 | static void |
1058 | rip_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 | 1070 | static void |
91c7c741 PM |
1071 | rip_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 | 1078 | static void |
875cc073 | 1079 | rip_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 |
1086 | static int |
1087 | rip_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 |
1092 | static int |
1093 | rip_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 |
1101 | static void |
1102 | rip_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 | 1111 | static struct proto * |
f4a60a9b | 1112 | rip_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 | 1130 | static int |
8465dccb | 1131 | rip_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 |
1155 | static int |
1156 | rip_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 | 1169 | static int |
f4a60a9b | 1170 | rip_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 | 1197 | static void |
13c0be19 | 1198 | rip_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 | 1206 | static int |
258be565 | 1207 | rip_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 | 1224 | void |
fd9f0c06 | 1225 | rip_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 | 1262 | void |
fd9f0c06 | 1263 | rip_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 | 1299 | static void |
8465dccb | 1300 | rip_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 | 1329 | struct 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 | }; |