]>
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 | |
8465dccb | 371 | |
4c5f93d7 | 372 | /* |
8465dccb | 373 | * RIP neighbors |
b093c328 | 374 | */ |
8465dccb OZ |
375 | |
376 | struct rip_neighbor * | |
377 | rip_get_neighbor(struct rip_proto *p, ip_addr *a, struct rip_iface *ifa) | |
378 | { | |
586c1800 | 379 | neighbor *nbr = neigh_find(&p->p, *a, ifa->iface, 0); |
8465dccb OZ |
380 | |
381 | if (!nbr || (nbr->scope == SCOPE_HOST) || !rip_iface_link_up(ifa)) | |
382 | return NULL; | |
383 | ||
384 | if (nbr->data) | |
385 | return nbr->data; | |
386 | ||
387 | TRACE(D_EVENTS, "New neighbor %I on %s", *a, ifa->iface->name); | |
388 | ||
389 | struct rip_neighbor *n = mb_allocz(p->p.pool, sizeof(struct rip_neighbor)); | |
390 | n->ifa = ifa; | |
391 | n->nbr = nbr; | |
392 | nbr->data = n; | |
393 | n->csn = nbr->aux; | |
394 | ||
395 | add_tail(&ifa->neigh_list, NODE n); | |
396 | ||
397 | return n; | |
398 | } | |
399 | ||
a103373f | 400 | static void |
8465dccb | 401 | rip_remove_neighbor(struct rip_proto *p, struct rip_neighbor *n) |
a103373f | 402 | { |
8465dccb OZ |
403 | neighbor *nbr = n->nbr; |
404 | ||
3343088a | 405 | TRACE(D_EVENTS, "Removing neighbor %I on %s", nbr->addr, nbr->ifreq->name); |
a103373f | 406 | |
8465dccb OZ |
407 | rem_node(NODE n); |
408 | n->ifa = NULL; | |
409 | n->nbr = NULL; | |
410 | nbr->data = NULL; | |
411 | nbr->aux = n->csn; | |
412 | ||
413 | rfree(n->bfd_req); | |
414 | n->bfd_req = NULL; | |
415 | n->last_seen = 0; | |
416 | ||
417 | if (!n->uc) | |
418 | mb_free(n); | |
419 | ||
420 | /* Related routes are removed in rip_timer() */ | |
421 | rip_kick_timer(p); | |
a103373f | 422 | } |
a103373f | 423 | |
8465dccb OZ |
424 | static inline void |
425 | rip_lock_neighbor(struct rip_neighbor *n) | |
426 | { | |
427 | n->uc++; | |
428 | } | |
279f4c7b | 429 | |
8465dccb OZ |
430 | static inline void |
431 | rip_unlock_neighbor(struct rip_neighbor *n) | |
a103373f | 432 | { |
8465dccb OZ |
433 | n->uc--; |
434 | ||
435 | if (!n->nbr && !n->uc) | |
436 | mb_free(n); | |
437 | } | |
438 | ||
439 | static void | |
440 | rip_neigh_notify(struct neighbor *nbr) | |
441 | { | |
442 | struct rip_proto *p = (struct rip_proto *) nbr->proto; | |
443 | struct rip_neighbor *n = nbr->data; | |
444 | ||
445 | if (!n) | |
446 | return; | |
447 | ||
448 | /* | |
449 | * We assume that rip_neigh_notify() is called before rip_if_notify() for | |
450 | * IF_CHANGE_DOWN and therefore n->ifa is still valid. We have no such | |
451 | * ordering assumption for IF_CHANGE_LINK, so we test link state of the | |
452 | * underlying iface instead of just rip_iface state. | |
453 | */ | |
454 | if ((nbr->scope <= 0) || !rip_iface_link_up(n->ifa)) | |
455 | rip_remove_neighbor(p, n); | |
456 | } | |
457 | ||
458 | static void | |
459 | rip_bfd_notify(struct bfd_request *req) | |
460 | { | |
461 | struct rip_neighbor *n = req->data; | |
462 | struct rip_proto *p = n->ifa->rip; | |
a103373f | 463 | |
8465dccb OZ |
464 | if (req->down) |
465 | { | |
466 | TRACE(D_EVENTS, "BFD session down for nbr %I on %s", | |
467 | n->nbr->addr, n->ifa->iface->name); | |
468 | rip_remove_neighbor(p, n); | |
a103373f | 469 | } |
8465dccb OZ |
470 | } |
471 | ||
472 | void | |
473 | rip_update_bfd(struct rip_proto *p, struct rip_neighbor *n) | |
474 | { | |
475 | int use_bfd = n->ifa->cf->bfd && n->last_seen; | |
a103373f | 476 | |
8465dccb OZ |
477 | if (use_bfd && !n->bfd_req) |
478 | { | |
479 | /* | |
480 | * For RIPv2, use the same address as rip_open_socket(). For RIPng, neighbor | |
481 | * should contain an address from the same prefix, thus also link-local. It | |
482 | * may cause problems if two link-local addresses are assigned to one iface. | |
483 | */ | |
484 | ip_addr saddr = rip_is_v2(p) ? n->ifa->sk->saddr : n->nbr->ifa->ip; | |
485 | n->bfd_req = bfd_request_session(p->p.pool, n->nbr->addr, saddr, | |
cf7ff995 OZ |
486 | n->nbr->iface, p->p.vrf, |
487 | rip_bfd_notify, n); | |
a103373f PM |
488 | } |
489 | ||
8465dccb OZ |
490 | if (!use_bfd && n->bfd_req) |
491 | { | |
492 | rfree(n->bfd_req); | |
493 | n->bfd_req = NULL; | |
494 | } | |
a103373f PM |
495 | } |
496 | ||
8465dccb | 497 | |
4c5f93d7 | 498 | /* |
8465dccb | 499 | * RIP interfaces |
b093c328 | 500 | */ |
8465dccb OZ |
501 | |
502 | static void | |
503 | rip_iface_start(struct rip_iface *ifa) | |
a103373f | 504 | { |
8465dccb | 505 | struct rip_proto *p = ifa->rip; |
a103373f | 506 | |
8465dccb | 507 | TRACE(D_EVENTS, "Starting interface %s", ifa->iface->name); |
4a020137 | 508 | |
92cc1e74 OZ |
509 | ifa->next_regular = current_time() + (random() % ifa->cf->update_time) + 100 MS; |
510 | ifa->next_triggered = current_time(); /* Available immediately */ | |
511 | ifa->want_triggered = 1; /* All routes in triggered update */ | |
a6f79ca5 | 512 | tm_start(ifa->timer, 100 MS); |
8465dccb | 513 | ifa->up = 1; |
f7615037 | 514 | |
8465dccb OZ |
515 | if (!ifa->cf->passive) |
516 | rip_send_request(ifa->rip, ifa); | |
517 | } | |
f7615037 | 518 | |
8465dccb OZ |
519 | static void |
520 | rip_iface_stop(struct rip_iface *ifa) | |
521 | { | |
522 | struct rip_proto *p = ifa->rip; | |
523 | struct rip_neighbor *n; | |
70e212f9 | 524 | |
8465dccb | 525 | TRACE(D_EVENTS, "Stopping interface %s", ifa->iface->name); |
70e212f9 | 526 | |
8465dccb | 527 | rip_reset_tx_session(p, ifa); |
a103373f | 528 | |
8465dccb OZ |
529 | WALK_LIST_FIRST(n, ifa->neigh_list) |
530 | rip_remove_neighbor(p, n); | |
0bff946c | 531 | |
a6f79ca5 | 532 | tm_stop(ifa->timer); |
8465dccb | 533 | ifa->up = 0; |
a103373f PM |
534 | } |
535 | ||
8465dccb OZ |
536 | static inline int |
537 | rip_iface_link_up(struct rip_iface *ifa) | |
538 | { | |
539 | return !ifa->cf->check_link || (ifa->iface->flags & IF_LINK_UP); | |
540 | } | |
279f4c7b | 541 | |
a103373f | 542 | static void |
8465dccb | 543 | rip_iface_update_state(struct rip_iface *ifa) |
a103373f | 544 | { |
8465dccb | 545 | int up = ifa->sk && rip_iface_link_up(ifa); |
a103373f | 546 | |
8465dccb OZ |
547 | if (up == ifa->up) |
548 | return; | |
549 | ||
550 | if (up) | |
551 | rip_iface_start(ifa); | |
552 | else | |
553 | rip_iface_stop(ifa); | |
554 | } | |
4c5f93d7 | 555 | |
a103373f | 556 | static void |
8465dccb | 557 | rip_iface_update_buffers(struct rip_iface *ifa) |
a103373f | 558 | { |
8465dccb OZ |
559 | if (!ifa->sk) |
560 | return; | |
a103373f | 561 | |
8465dccb OZ |
562 | uint rbsize = ifa->cf->rx_buffer ?: ifa->iface->mtu; |
563 | uint tbsize = ifa->cf->tx_length ?: ifa->iface->mtu; | |
564 | rbsize = MAX(rbsize, tbsize); | |
de41dcd1 | 565 | |
8465dccb OZ |
566 | sk_set_rbsize(ifa->sk, rbsize); |
567 | sk_set_tbsize(ifa->sk, tbsize); | |
de41dcd1 | 568 | |
8465dccb OZ |
569 | uint headers = (rip_is_v2(ifa->rip) ? IP4_HEADER_LENGTH : IP6_HEADER_LENGTH) + UDP_HEADER_LENGTH; |
570 | ifa->tx_plen = tbsize - headers; | |
feb6abe0 | 571 | |
8465dccb | 572 | if (ifa->cf->auth_type == RIP_AUTH_CRYPTO) |
390601f0 | 573 | ifa->tx_plen -= RIP_AUTH_TAIL_LENGTH + max_mac_length(ifa->cf->passwords); |
8465dccb | 574 | } |
3918b1b0 | 575 | |
8465dccb OZ |
576 | static inline void |
577 | rip_iface_update_bfd(struct rip_iface *ifa) | |
578 | { | |
579 | struct rip_proto *p = ifa->rip; | |
580 | struct rip_neighbor *n; | |
581 | ||
582 | WALK_LIST(n, ifa->neigh_list) | |
583 | rip_update_bfd(p, n); | |
584 | } | |
585 | ||
586 | ||
587 | static void | |
588 | rip_iface_locked(struct object_lock *lock) | |
589 | { | |
590 | struct rip_iface *ifa = lock->data; | |
591 | struct rip_proto *p = ifa->rip; | |
279f4c7b | 592 | |
8465dccb | 593 | if (!rip_open_socket(ifa)) |
feb6abe0 | 594 | { |
8465dccb OZ |
595 | log(L_ERR "%s: Cannot open socket for %s", p->p.name, ifa->iface->name); |
596 | return; | |
597 | } | |
a9c38203 | 598 | |
8465dccb OZ |
599 | rip_iface_update_buffers(ifa); |
600 | rip_iface_update_state(ifa); | |
601 | } | |
a9c38203 | 602 | |
279f4c7b | 603 | |
8465dccb OZ |
604 | static struct rip_iface * |
605 | rip_find_iface(struct rip_proto *p, struct iface *what) | |
606 | { | |
607 | struct rip_iface *ifa; | |
feb6abe0 | 608 | |
8465dccb OZ |
609 | WALK_LIST(ifa, p->iface_list) |
610 | if (ifa->iface == what) | |
611 | return ifa; | |
1d941de4 | 612 | |
8465dccb | 613 | return NULL; |
a103373f PM |
614 | } |
615 | ||
8465dccb OZ |
616 | static void |
617 | rip_add_iface(struct rip_proto *p, struct iface *iface, struct rip_iface_config *ic) | |
a103373f | 618 | { |
8465dccb OZ |
619 | struct rip_iface *ifa; |
620 | ||
621 | TRACE(D_EVENTS, "Adding interface %s", iface->name); | |
622 | ||
623 | ifa = mb_allocz(p->p.pool, sizeof(struct rip_iface)); | |
624 | ifa->rip = p; | |
625 | ifa->iface = iface; | |
626 | ifa->cf = ic; | |
627 | ||
628 | if (ipa_nonzero(ic->address)) | |
629 | ifa->addr = ic->address; | |
630 | else if (ic->mode == RIP_IM_MULTICAST) | |
631 | ifa->addr = rip_is_v2(p) ? IP4_RIP_ROUTERS : IP6_RIP_ROUTERS; | |
632 | else /* Broadcast */ | |
153f02da OZ |
633 | ifa->addr = iface->addr4->brd; |
634 | /* | |
635 | * The above is just a workaround for BSD as it can't send broadcasts | |
636 | * to 255.255.255.255. BSD systems need the network broadcast address instead. | |
637 | * | |
638 | * TODO: move this to sysdep code | |
639 | */ | |
8465dccb OZ |
640 | |
641 | init_list(&ifa->neigh_list); | |
642 | ||
643 | add_tail(&p->iface_list, NODE ifa); | |
644 | ||
a6f79ca5 | 645 | ifa->timer = tm_new_init(p->p.pool, rip_iface_timer, ifa, 0, 0); |
8465dccb OZ |
646 | |
647 | struct object_lock *lock = olock_new(p->p.pool); | |
648 | lock->type = OBJLOCK_UDP; | |
649 | lock->port = ic->port; | |
650 | lock->iface = iface; | |
651 | lock->data = ifa; | |
652 | lock->hook = rip_iface_locked; | |
653 | ifa->lock = lock; | |
654 | ||
655 | olock_acquire(lock); | |
a103373f PM |
656 | } |
657 | ||
a103373f | 658 | static void |
8465dccb | 659 | rip_remove_iface(struct rip_proto *p, struct rip_iface *ifa) |
a103373f | 660 | { |
8465dccb | 661 | rip_iface_stop(ifa); |
c748cdb9 | 662 | |
8465dccb OZ |
663 | TRACE(D_EVENTS, "Removing interface %s", ifa->iface->name); |
664 | ||
665 | rem_node(NODE ifa); | |
666 | ||
667 | rfree(ifa->sk); | |
668 | rfree(ifa->lock); | |
669 | rfree(ifa->timer); | |
670 | ||
671 | mb_free(ifa); | |
672 | } | |
673 | ||
674 | static int | |
675 | rip_reconfigure_iface(struct rip_proto *p, struct rip_iface *ifa, struct rip_iface_config *new) | |
676 | { | |
677 | struct rip_iface_config *old = ifa->cf; | |
678 | ||
679 | /* Change of these options would require to reset the iface socket */ | |
680 | if ((new->mode != old->mode) || | |
681 | (new->port != old->port) || | |
682 | (new->tx_tos != old->tx_tos) || | |
683 | (new->tx_priority != old->tx_priority) || | |
684 | (new->ttl_security != old->ttl_security)) | |
685 | return 0; | |
686 | ||
687 | TRACE(D_EVENTS, "Reconfiguring interface %s", ifa->iface->name); | |
688 | ||
689 | ifa->cf = new; | |
690 | ||
390601f0 OZ |
691 | rip_iface_update_buffers(ifa); |
692 | ||
92cc1e74 OZ |
693 | if (ifa->next_regular > (current_time() + new->update_time)) |
694 | ifa->next_regular = current_time() + (random() % new->update_time) + 100 MS; | |
8465dccb | 695 | |
8465dccb OZ |
696 | if (new->check_link != old->check_link) |
697 | rip_iface_update_state(ifa); | |
698 | ||
699 | if (new->bfd != old->bfd) | |
700 | rip_iface_update_bfd(ifa); | |
701 | ||
702 | if (ifa->up) | |
703 | rip_iface_kick_timer(ifa); | |
704 | ||
705 | return 1; | |
c3e9b2ab PM |
706 | } |
707 | ||
dff1f579 | 708 | static void |
8465dccb | 709 | rip_reconfigure_ifaces(struct rip_proto *p, struct rip_config *cf) |
dff1f579 | 710 | { |
8465dccb OZ |
711 | struct iface *iface; |
712 | ||
713 | WALK_LIST(iface, iface_list) | |
714 | { | |
153f02da OZ |
715 | if (!(iface->flags & IF_UP)) |
716 | continue; | |
717 | ||
718 | /* Ignore ifaces without appropriate address */ | |
719 | if (rip_is_v2(p) ? !iface->addr4 : !iface->llv6) | |
8465dccb OZ |
720 | continue; |
721 | ||
722 | struct rip_iface *ifa = rip_find_iface(p, iface); | |
723 | struct rip_iface_config *ic = (void *) iface_patt_find(&cf->patt_list, iface, NULL); | |
ce1da96e | 724 | |
8465dccb OZ |
725 | if (ifa && ic) |
726 | { | |
727 | if (rip_reconfigure_iface(p, ifa, ic)) | |
728 | continue; | |
729 | ||
730 | /* Hard restart */ | |
731 | log(L_INFO "%s: Restarting interface %s", p->p.name, ifa->iface->name); | |
732 | rip_remove_iface(p, ifa); | |
733 | rip_add_iface(p, iface, ic); | |
734 | } | |
735 | ||
736 | if (ifa && !ic) | |
737 | rip_remove_iface(p, ifa); | |
738 | ||
739 | if (!ifa && ic) | |
740 | rip_add_iface(p, iface, ic); | |
741 | } | |
dff1f579 PM |
742 | } |
743 | ||
c3e9b2ab | 744 | static void |
8465dccb | 745 | rip_if_notify(struct proto *P, unsigned flags, struct iface *iface) |
c3e9b2ab | 746 | { |
8465dccb OZ |
747 | struct rip_proto *p = (void *) P; |
748 | struct rip_config *cf = (void *) P->cf; | |
e2ae0869 | 749 | struct rip_iface *ifa = rip_find_iface(p, iface); |
8465dccb OZ |
750 | |
751 | if (iface->flags & IF_IGNORE) | |
752 | return; | |
753 | ||
e2ae0869 OZ |
754 | /* Add, remove or restart interface */ |
755 | if (flags & (IF_CHANGE_UPDOWN | (rip_is_v2(p) ? IF_CHANGE_ADDR4 : IF_CHANGE_LLV6))) | |
8465dccb | 756 | { |
e2ae0869 OZ |
757 | if (ifa) |
758 | rip_remove_iface(p, ifa); | |
759 | ||
760 | if (!(iface->flags & IF_UP)) | |
761 | return; | |
8465dccb | 762 | |
153f02da OZ |
763 | /* Ignore ifaces without appropriate address */ |
764 | if (rip_is_v2(p) ? !iface->addr4 : !iface->llv6) | |
765 | return; | |
766 | ||
e2ae0869 | 767 | struct rip_iface_config *ic = (void *) iface_patt_find(&cf->patt_list, iface, NULL); |
8465dccb OZ |
768 | if (ic) |
769 | rip_add_iface(p, iface, ic); | |
770 | ||
771 | return; | |
772 | } | |
773 | ||
8465dccb OZ |
774 | if (!ifa) |
775 | return; | |
776 | ||
8465dccb OZ |
777 | if (flags & IF_CHANGE_MTU) |
778 | rip_iface_update_buffers(ifa); | |
779 | ||
780 | if (flags & IF_CHANGE_LINK) | |
781 | rip_iface_update_state(ifa); | |
c3e9b2ab PM |
782 | } |
783 | ||
8465dccb OZ |
784 | |
785 | /* | |
786 | * RIP timer events | |
787 | */ | |
788 | ||
6c0a7174 | 789 | /** |
8465dccb OZ |
790 | * rip_timer - RIP main timer hook |
791 | * @t: timer | |
4c5f93d7 | 792 | * |
8465dccb OZ |
793 | * The RIP main timer is responsible for routing table maintenance. Invalid or |
794 | * expired routes (&rip_rte) are removed and garbage collection of stale routing | |
795 | * table entries (&rip_entry) is done. Changes are propagated to core tables, | |
796 | * route reload is also done here. Note that garbage collection uses a maximal | |
797 | * GC time, while interfaces maintain an illusion of per-interface GC times in | |
798 | * rip_send_response(). | |
799 | * | |
800 | * Keeping incoming routes and the selected outgoing route are two independent | |
801 | * functions, therefore after garbage collection some entries now considered | |
802 | * invalid (RIP_ENTRY_DUMMY) still may have non-empty list of incoming routes, | |
803 | * while some valid entries (representing an outgoing route) may have that list | |
804 | * empty. | |
805 | * | |
806 | * The main timer is not scheduled periodically but it uses the time of the | |
807 | * current next event and the minimal interval of any possible event to compute | |
808 | * the time of the next run. | |
06fa1453 | 809 | */ |
8465dccb OZ |
810 | static void |
811 | rip_timer(timer *t) | |
c3e9b2ab | 812 | { |
8465dccb OZ |
813 | struct rip_proto *p = t->data; |
814 | struct rip_config *cf = (void *) (p->p.cf); | |
815 | struct rip_iface *ifa; | |
816 | struct rip_neighbor *n, *nn; | |
817 | struct fib_iterator fit; | |
92cc1e74 OZ |
818 | btime now_ = current_time(); |
819 | btime next = now_ + MIN(cf->min_timeout_time, cf->max_garbage_time); | |
820 | btime expires = 0; | |
8465dccb OZ |
821 | |
822 | TRACE(D_EVENTS, "Main timer fired"); | |
823 | ||
824 | FIB_ITERATE_INIT(&fit, &p->rtable); | |
825 | ||
826 | loop: | |
600998fc | 827 | FIB_ITERATE_START(&p->rtable, &fit, struct rip_entry, en) |
8465dccb | 828 | { |
8465dccb OZ |
829 | struct rip_rte *rt, **rp; |
830 | int changed = 0; | |
831 | ||
832 | /* Checking received routes for timeout and for dead neighbors */ | |
833 | for (rp = &en->routes; rt = *rp; /* rp = &rt->next */) | |
e24ddd9b | 834 | { |
92cc1e74 | 835 | if (!rip_valid_rte(rt) || (rt->expires <= now_)) |
8465dccb OZ |
836 | { |
837 | rip_remove_rte(p, rp); | |
838 | changed = 1; | |
839 | continue; | |
840 | } | |
06fa1453 | 841 | |
8465dccb OZ |
842 | next = MIN(next, rt->expires); |
843 | rp = &rt->next; | |
28323d9d | 844 | } |
06fa1453 | 845 | |
8465dccb OZ |
846 | /* Propagating eventual change */ |
847 | if (changed || p->rt_reload) | |
848 | { | |
849 | /* | |
850 | * We have to restart the iteration because there may be a cascade of | |
851 | * synchronous events rip_announce_rte() -> nest table change -> | |
852 | * rip_rt_notify() -> p->rtable change, invalidating hidden variables. | |
853 | */ | |
854 | ||
600998fc | 855 | FIB_ITERATE_PUT_NEXT(&fit, &p->rtable); |
8465dccb OZ |
856 | rip_announce_rte(p, en); |
857 | goto loop; | |
858 | } | |
f9c799a0 | 859 | |
8465dccb OZ |
860 | /* Checking stale entries for garbage collection timeout */ |
861 | if (en->valid == RIP_ENTRY_STALE) | |
862 | { | |
863 | expires = en->changed + cf->max_garbage_time; | |
f9c799a0 | 864 | |
92cc1e74 | 865 | if (expires <= now_) |
f9c799a0 | 866 | { |
fe9f1a6d | 867 | // TRACE(D_EVENTS, "entry is too old: %N", en->n.addr); |
8465dccb | 868 | en->valid = 0; |
f9c799a0 | 869 | } |
8465dccb OZ |
870 | else |
871 | next = MIN(next, expires); | |
872 | } | |
873 | ||
874 | /* Remove empty nodes */ | |
875 | if (!en->valid && !en->routes) | |
876 | { | |
600998fc OZ |
877 | FIB_ITERATE_PUT(&fit); |
878 | fib_delete(&p->rtable, en); | |
8465dccb OZ |
879 | goto loop; |
880 | } | |
881 | } | |
600998fc | 882 | FIB_ITERATE_END; |
8465dccb OZ |
883 | |
884 | p->rt_reload = 0; | |
885 | ||
886 | /* Handling neighbor expiration */ | |
887 | WALK_LIST(ifa, p->iface_list) | |
888 | WALK_LIST_DELSAFE(n, nn, ifa->neigh_list) | |
889 | if (n->last_seen) | |
f9c799a0 | 890 | { |
8465dccb OZ |
891 | expires = n->last_seen + n->ifa->cf->timeout_time; |
892 | ||
92cc1e74 | 893 | if (expires <= now_) |
8465dccb OZ |
894 | rip_remove_neighbor(p, n); |
895 | else | |
896 | next = MIN(next, expires); | |
f9c799a0 | 897 | } |
b94bbe00 | 898 | |
a6f79ca5 | 899 | tm_start(p->timer, MAX(next - now_, 100 MS)); |
8465dccb OZ |
900 | } |
901 | ||
902 | static inline void | |
903 | rip_kick_timer(struct rip_proto *p) | |
904 | { | |
92cc1e74 | 905 | if (p->timer->expires > (current_time() + 100 MS)) |
a6f79ca5 | 906 | tm_start(p->timer, 100 MS); |
8465dccb | 907 | } |
f9c799a0 | 908 | |
8465dccb OZ |
909 | /** |
910 | * rip_iface_timer - RIP interface timer hook | |
911 | * @t: timer | |
912 | * | |
913 | * RIP interface timers are responsible for scheduling both regular and | |
914 | * triggered updates. Fixed, delay-independent period is used for regular | |
915 | * updates, while minimal separating interval is enforced for triggered updates. | |
916 | * The function also ensures that a new update is not started when the old one | |
917 | * is still running. | |
918 | */ | |
919 | static void | |
920 | rip_iface_timer(timer *t) | |
921 | { | |
922 | struct rip_iface *ifa = t->data; | |
923 | struct rip_proto *p = ifa->rip; | |
92cc1e74 OZ |
924 | btime now_ = current_time(); |
925 | btime period = ifa->cf->update_time; | |
8465dccb OZ |
926 | |
927 | if (ifa->cf->passive) | |
928 | return; | |
929 | ||
930 | TRACE(D_EVENTS, "Interface timer fired for %s", ifa->iface->name); | |
931 | ||
932 | if (ifa->tx_active) | |
933 | { | |
92cc1e74 | 934 | if (now_ < (ifa->next_regular + period)) |
a6f79ca5 | 935 | { tm_start(ifa->timer, 100 MS); return; } |
8465dccb OZ |
936 | |
937 | /* We are too late, reset is done by rip_send_table() */ | |
938 | log(L_WARN "%s: Too slow update on %s, resetting", p->p.name, ifa->iface->name); | |
f9c799a0 | 939 | } |
8465dccb | 940 | |
92cc1e74 | 941 | if (now_ >= ifa->next_regular) |
8465dccb OZ |
942 | { |
943 | /* Send regular update, set timer for next period (or following one if necessay) */ | |
944 | TRACE(D_EVENTS, "Sending regular updates for %s", ifa->iface->name); | |
945 | rip_send_table(p, ifa, ifa->addr, 0); | |
92cc1e74 | 946 | ifa->next_regular += period * (1 + ((now_ - ifa->next_regular) / period)); |
8465dccb OZ |
947 | ifa->want_triggered = 0; |
948 | p->triggered = 0; | |
949 | } | |
92cc1e74 | 950 | else if (ifa->want_triggered && (now_ >= ifa->next_triggered)) |
8465dccb OZ |
951 | { |
952 | /* Send triggered update, enforce interval between triggered updates */ | |
953 | TRACE(D_EVENTS, "Sending triggered updates for %s", ifa->iface->name); | |
954 | rip_send_table(p, ifa, ifa->addr, ifa->want_triggered); | |
92cc1e74 | 955 | ifa->next_triggered = now_ + MIN(5 S, period / 2); |
8465dccb OZ |
956 | ifa->want_triggered = 0; |
957 | p->triggered = 0; | |
958 | } | |
959 | ||
a6f79ca5 | 960 | tm_start(ifa->timer, ifa->want_triggered ? (1 S) : (ifa->next_regular - now_)); |
a103373f PM |
961 | } |
962 | ||
8465dccb OZ |
963 | static inline void |
964 | rip_iface_kick_timer(struct rip_iface *ifa) | |
ff8ed632 | 965 | { |
92cc1e74 | 966 | if (ifa->timer->expires > (current_time() + 100 MS)) |
a6f79ca5 | 967 | tm_start(ifa->timer, 100 MS); |
ff8ed632 PM |
968 | } |
969 | ||
279f4c7b | 970 | static void |
8465dccb | 971 | rip_trigger_update(struct rip_proto *p) |
279f4c7b | 972 | { |
8465dccb | 973 | if (p->triggered) |
9a158361 | 974 | return; |
8465dccb OZ |
975 | |
976 | struct rip_iface *ifa; | |
977 | WALK_LIST(ifa, p->iface_list) | |
978 | { | |
979 | /* Interface not active */ | |
980 | if (! ifa->up) | |
981 | continue; | |
982 | ||
983 | /* Already scheduled */ | |
984 | if (ifa->want_triggered) | |
985 | continue; | |
986 | ||
987 | TRACE(D_EVENTS, "Scheduling triggered updates for %s", ifa->iface->name); | |
92cc1e74 | 988 | ifa->want_triggered = current_time(); |
8465dccb | 989 | rip_iface_kick_timer(ifa); |
c3e9b2ab | 990 | } |
8465dccb OZ |
991 | |
992 | p->triggered = 1; | |
279f4c7b PM |
993 | } |
994 | ||
8465dccb OZ |
995 | |
996 | /* | |
997 | * RIP protocol glue | |
998 | */ | |
999 | ||
f4a60a9b OZ |
1000 | static void |
1001 | rip_reload_routes(struct channel *C) | |
8465dccb | 1002 | { |
f4a60a9b | 1003 | struct rip_proto *p = (struct rip_proto *) C->proto; |
8465dccb OZ |
1004 | |
1005 | if (p->rt_reload) | |
f4a60a9b | 1006 | return; |
8465dccb OZ |
1007 | |
1008 | TRACE(D_EVENTS, "Scheduling route reload"); | |
1009 | p->rt_reload = 1; | |
1010 | rip_kick_timer(p); | |
8465dccb OZ |
1011 | } |
1012 | ||
875cc073 | 1013 | static void |
91c7c741 PM |
1014 | rip_make_tmp_attrs(struct rte *rt, struct linpool *pool) |
1015 | { | |
875cc073 OZ |
1016 | rte_init_tmp_attrs(rt, pool, 2); |
1017 | rte_make_tmp_attr(rt, EA_RIP_METRIC, EAF_TYPE_INT, rt->u.rip.metric); | |
1018 | rte_make_tmp_attr(rt, EA_RIP_TAG, EAF_TYPE_INT, rt->u.rip.tag); | |
91c7c741 PM |
1019 | } |
1020 | ||
8465dccb | 1021 | static void |
875cc073 | 1022 | rip_store_tmp_attrs(struct rte *rt, struct linpool *pool) |
91c7c741 | 1023 | { |
875cc073 OZ |
1024 | rte_init_tmp_attrs(rt, pool, 2); |
1025 | rt->u.rip.metric = rte_store_tmp_attr(rt, EA_RIP_METRIC); | |
1026 | rt->u.rip.tag = rte_store_tmp_attr(rt, EA_RIP_TAG); | |
91c7c741 PM |
1027 | } |
1028 | ||
8465dccb OZ |
1029 | static int |
1030 | rip_rte_better(struct rte *new, struct rte *old) | |
1d941de4 | 1031 | { |
8465dccb | 1032 | return new->u.rip.metric < old->u.rip.metric; |
1d941de4 PM |
1033 | } |
1034 | ||
16c2d48d OF |
1035 | static int |
1036 | rip_rte_same(struct rte *new, struct rte *old) | |
1037 | { | |
8465dccb OZ |
1038 | return ((new->u.rip.metric == old->u.rip.metric) && |
1039 | (new->u.rip.tag == old->u.rip.tag) && | |
1040 | (new->u.rip.from == old->u.rip.from)); | |
16c2d48d OF |
1041 | } |
1042 | ||
1043 | ||
f4a60a9b OZ |
1044 | static void |
1045 | rip_postconfig(struct proto_config *CF) | |
1046 | { | |
1047 | // struct rip_config *cf = (void *) CF; | |
1048 | ||
1049 | /* Define default channel */ | |
1050 | if (EMPTY_LIST(CF->channels)) | |
72163bd5 | 1051 | channel_config_new(NULL, net_label[CF->net_type], CF->net_type, CF); |
f4a60a9b OZ |
1052 | } |
1053 | ||
8465dccb | 1054 | static struct proto * |
f4a60a9b | 1055 | rip_init(struct proto_config *CF) |
8465dccb | 1056 | { |
f4a60a9b OZ |
1057 | struct proto *P = proto_new(CF); |
1058 | ||
1059 | P->main_channel = proto_add_channel(P, proto_cf_main_channel(CF)); | |
8465dccb | 1060 | |
8465dccb OZ |
1061 | P->if_notify = rip_if_notify; |
1062 | P->rt_notify = rip_rt_notify; | |
1063 | P->neigh_notify = rip_neigh_notify; | |
8465dccb OZ |
1064 | P->reload_routes = rip_reload_routes; |
1065 | P->make_tmp_attrs = rip_make_tmp_attrs; | |
1066 | P->store_tmp_attrs = rip_store_tmp_attrs; | |
1067 | P->rte_better = rip_rte_better; | |
1068 | P->rte_same = rip_rte_same; | |
1069 | ||
1070 | return P; | |
1071 | } | |
1072 | ||
feb6abe0 | 1073 | static int |
8465dccb | 1074 | rip_start(struct proto *P) |
21580e30 | 1075 | { |
8465dccb OZ |
1076 | struct rip_proto *p = (void *) P; |
1077 | struct rip_config *cf = (void *) (P->cf); | |
8d2e3eba | 1078 | |
8465dccb | 1079 | init_list(&p->iface_list); |
23c212e7 | 1080 | fib_init(&p->rtable, P->pool, cf->rip2 ? NET_IP4 : NET_IP6, |
fe9f1a6d | 1081 | sizeof(struct rip_entry), OFFSETOF(struct rip_entry, n), 0, NULL); |
8465dccb | 1082 | p->rte_slab = sl_new(P->pool, sizeof(struct rip_rte)); |
a6f79ca5 | 1083 | p->timer = tm_new_init(P->pool, rip_timer, p, 0, 0); |
3918b1b0 | 1084 | |
23c212e7 | 1085 | p->rip2 = cf->rip2; |
8465dccb OZ |
1086 | p->ecmp = cf->ecmp; |
1087 | p->infinity = cf->infinity; | |
1088 | p->triggered = 0; | |
21580e30 | 1089 | |
8465dccb OZ |
1090 | p->log_pkt_tbf = (struct tbf){ .rate = 1, .burst = 5 }; |
1091 | p->log_rte_tbf = (struct tbf){ .rate = 4, .burst = 20 }; | |
21580e30 | 1092 | |
a6f79ca5 | 1093 | tm_start(p->timer, MIN(cf->min_timeout_time, cf->max_garbage_time)); |
21580e30 | 1094 | |
8465dccb | 1095 | return PS_UP; |
21580e30 PM |
1096 | } |
1097 | ||
8465dccb | 1098 | static int |
f4a60a9b | 1099 | rip_reconfigure(struct proto *P, struct proto_config *CF) |
feb6abe0 | 1100 | { |
8465dccb | 1101 | struct rip_proto *p = (void *) P; |
f4a60a9b | 1102 | struct rip_config *new = (void *) CF; |
8465dccb | 1103 | // struct rip_config *old = (void *) (P->cf); |
feb6abe0 | 1104 | |
23c212e7 OZ |
1105 | if (new->rip2 != p->rip2) |
1106 | return 0; | |
1107 | ||
8465dccb OZ |
1108 | if (new->infinity != p->infinity) |
1109 | return 0; | |
feb6abe0 | 1110 | |
f4a60a9b OZ |
1111 | if (!proto_configure_channel(P, &P->main_channel, proto_cf_main_channel(CF))) |
1112 | return 0; | |
1113 | ||
8465dccb OZ |
1114 | TRACE(D_EVENTS, "Reconfiguring"); |
1115 | ||
f4a60a9b | 1116 | p->p.cf = CF; |
8465dccb OZ |
1117 | p->ecmp = new->ecmp; |
1118 | rip_reconfigure_ifaces(p, new); | |
1119 | ||
1120 | p->rt_reload = 1; | |
1121 | rip_kick_timer(p); | |
1122 | ||
1123 | return 1; | |
c748cdb9 | 1124 | } |
2d9290e9 | 1125 | |
8465dccb | 1126 | static void |
13c0be19 | 1127 | rip_get_route_info(rte *rte, byte *buf) |
c748cdb9 | 1128 | { |
8465dccb OZ |
1129 | buf += bsprintf(buf, " (%d/%d)", rte->pref, rte->u.rip.metric); |
1130 | ||
1131 | if (rte->u.rip.tag) | |
1132 | bsprintf(buf, " [%04x]", rte->u.rip.tag); | |
a103373f PM |
1133 | } |
1134 | ||
a769a180 | 1135 | static int |
aebe06b4 | 1136 | rip_get_attr(eattr *a, byte *buf, int buflen UNUSED) |
a769a180 | 1137 | { |
8465dccb OZ |
1138 | switch (a->id) |
1139 | { | |
1140 | case EA_RIP_METRIC: | |
1141 | bsprintf(buf, "metric: %d", a->u.data); | |
1142 | return GA_FULL; | |
1143 | ||
1144 | case EA_RIP_TAG: | |
1145 | bsprintf(buf, "tag: %04x", a->u.data); | |
1146 | return GA_FULL; | |
1147 | ||
1148 | default: | |
1149 | return GA_UNKNOWN; | |
a769a180 PM |
1150 | } |
1151 | } | |
1152 | ||
8465dccb OZ |
1153 | void |
1154 | rip_show_interfaces(struct proto *P, char *iff) | |
898fdd85 | 1155 | { |
8465dccb OZ |
1156 | struct rip_proto *p = (void *) P; |
1157 | struct rip_iface *ifa = NULL; | |
1158 | struct rip_neighbor *n = NULL; | |
1159 | ||
1160 | if (p->p.proto_state != PS_UP) | |
1161 | { | |
1162 | cli_msg(-1021, "%s: is not up", p->p.name); | |
1163 | cli_msg(0, ""); | |
1164 | return; | |
1165 | } | |
1166 | ||
1167 | cli_msg(-1021, "%s:", p->p.name); | |
92cc1e74 | 1168 | cli_msg(-1021, "%-10s %-6s %6s %6s %7s", |
8465dccb OZ |
1169 | "Interface", "State", "Metric", "Nbrs", "Timer"); |
1170 | ||
1171 | WALK_LIST(ifa, p->iface_list) | |
1172 | { | |
1173 | if (iff && !patmatch(iff, ifa->iface->name)) | |
1174 | continue; | |
1175 | ||
1176 | int nbrs = 0; | |
1177 | WALK_LIST(n, ifa->neigh_list) | |
1178 | if (n->last_seen) | |
1179 | nbrs++; | |
1180 | ||
92cc1e74 OZ |
1181 | btime now_ = current_time(); |
1182 | btime timer = (ifa->next_regular > now_) ? (ifa->next_regular - now_) : 0; | |
1183 | cli_msg(-1021, "%-10s %-6s %6u %6u %7t", | |
8465dccb OZ |
1184 | ifa->iface->name, (ifa->up ? "Up" : "Down"), ifa->cf->metric, nbrs, timer); |
1185 | } | |
1186 | ||
1187 | cli_msg(0, ""); | |
898fdd85 PM |
1188 | } |
1189 | ||
8465dccb OZ |
1190 | void |
1191 | rip_show_neighbors(struct proto *P, char *iff) | |
7f5f44bb | 1192 | { |
8465dccb OZ |
1193 | struct rip_proto *p = (void *) P; |
1194 | struct rip_iface *ifa = NULL; | |
1195 | struct rip_neighbor *n = NULL; | |
7f5f44bb | 1196 | |
8465dccb OZ |
1197 | if (p->p.proto_state != PS_UP) |
1198 | { | |
1199 | cli_msg(-1022, "%s: is not up", p->p.name); | |
1200 | cli_msg(0, ""); | |
1201 | return; | |
1202 | } | |
1203 | ||
1204 | cli_msg(-1022, "%s:", p->p.name); | |
92cc1e74 | 1205 | cli_msg(-1022, "%-25s %-10s %6s %6s %7s", |
8465dccb OZ |
1206 | "IP address", "Interface", "Metric", "Routes", "Seen"); |
1207 | ||
1208 | WALK_LIST(ifa, p->iface_list) | |
1209 | { | |
1210 | if (iff && !patmatch(iff, ifa->iface->name)) | |
1211 | continue; | |
1212 | ||
1213 | WALK_LIST(n, ifa->neigh_list) | |
1214 | { | |
1215 | if (!n->last_seen) | |
1216 | continue; | |
1217 | ||
92cc1e74 OZ |
1218 | btime timer = current_time() - n->last_seen; |
1219 | cli_msg(-1022, "%-25I %-10s %6u %6u %7t", | |
8465dccb OZ |
1220 | n->nbr->addr, ifa->iface->name, ifa->cf->metric, n->uc, timer); |
1221 | } | |
1222 | } | |
1223 | ||
1224 | cli_msg(0, ""); | |
7f5f44bb PM |
1225 | } |
1226 | ||
a7f23f58 | 1227 | static void |
8465dccb | 1228 | rip_dump(struct proto *P) |
a7f23f58 | 1229 | { |
8465dccb OZ |
1230 | struct rip_proto *p = (struct rip_proto *) P; |
1231 | struct rip_iface *ifa; | |
1232 | int i; | |
a7f23f58 | 1233 | |
8465dccb | 1234 | i = 0; |
600998fc | 1235 | FIB_WALK(&p->rtable, struct rip_entry, en) |
8465dccb | 1236 | { |
92cc1e74 | 1237 | debug("RIP: entry #%d: %N via %I dev %s valid %d metric %d age %t\n", |
fe9f1a6d | 1238 | i++, en->n.addr, en->next_hop, en->iface->name, |
92cc1e74 | 1239 | en->valid, en->metric, current_time() - en->changed); |
8465dccb OZ |
1240 | } |
1241 | FIB_WALK_END; | |
a7f23f58 | 1242 | |
8465dccb OZ |
1243 | i = 0; |
1244 | WALK_LIST(ifa, p->iface_list) | |
1245 | { | |
1246 | debug("RIP: interface #%d: %s, %I, up = %d, busy = %d\n", | |
1247 | i++, ifa->iface->name, ifa->sk ? ifa->sk->daddr : IPA_NONE, | |
1248 | ifa->up, ifa->tx_active); | |
1249 | } | |
a7f23f58 OZ |
1250 | } |
1251 | ||
1252 | ||
a103373f | 1253 | struct protocol proto_rip = { |
4a591d4b PT |
1254 | .name = "RIP", |
1255 | .template = "rip%d", | |
ee7e2ffd | 1256 | .class = PROTOCOL_RIP, |
4a591d4b | 1257 | .preference = DEF_PREF_RIP, |
f4a60a9b OZ |
1258 | .channel_mask = NB_IP, |
1259 | .proto_size = sizeof(struct rip_proto), | |
8465dccb | 1260 | .config_size = sizeof(struct rip_config), |
f4a60a9b | 1261 | .postconfig = rip_postconfig, |
4a591d4b PT |
1262 | .init = rip_init, |
1263 | .dump = rip_dump, | |
1264 | .start = rip_start, | |
1265 | .reconfigure = rip_reconfigure, | |
2bbc3083 OZ |
1266 | .get_route_info = rip_get_route_info, |
1267 | .get_attr = rip_get_attr | |
a103373f | 1268 | }; |