]> git.ipfire.org Git - thirdparty/bird.git/blame - proto/radv/radv.c
Implements router advertisements activated by received routes.
[thirdparty/bird.git] / proto / radv / radv.c
CommitLineData
93e868c7
OZ
1/*
2 * BIRD -- Router Advertisement
3 *
4 *
5 * Can be freely distributed and used under the terms of the GNU GPL.
6 */
7
8
9#include <stdlib.h>
10#include "radv.h"
11
12/**
13 * DOC: Router Advertisements
14 *
15 * The RAdv protocol is implemented in two files: |radv.c| containing
16 * the interface with BIRD core and the protocol logic and |packets.c|
17 * handling low level protocol stuff (RX, TX and packet formats).
18 * The protocol does not import or export any routes.
19 *
20 * The RAdv is structured in the usual way - for each handled interface
21 * there is a structure &radv_iface that contains a state related to
22 * that interface together with its resources (a socket, a timer).
23 * There is also a prepared RA stored in a TX buffer of the socket
24 * associated with an iface. These iface structures are created
25 * and removed according to iface events from BIRD core handled by
26 * radv_if_notify() callback.
27 *
28 * The main logic of RAdv consists of two functions:
29 * radv_iface_notify(), which processes asynchronous events (specified
30 * by RA_EV_* codes), and radv_timer(), which triggers sending RAs and
31 * computes the next timeout.
fc06fb62 32 *
36da2857
OZ
33 * The RAdv protocol could receive routes (through
34 * radv_import_control() and radv_rt_notify()), but only the
35 * configured trigger route is tracked (in &active var). When a radv
36 * protocol is reconfigured, the connected routing table is examined
37 * (in radv_check_active()) to have proper &active value in case of
38 * the specified trigger prefix was changed.
39 *
fc06fb62
OZ
40 * Supported standards:
41 * - RFC 4861 - main RA standard
42 * - RFC 6106 - DNS extensions (RDDNS, DNSSL)
93e868c7
OZ
43 */
44
45static void
46radv_timer(timer *tm)
47{
48 struct radv_iface *ifa = tm->data;
49 struct proto_radv *ra = ifa->ra;
50
51 RADV_TRACE(D_EVENTS, "Timer fired on %s", ifa->iface->name);
52
53 radv_send_ra(ifa, 0);
54
55 /* Update timer */
56 ifa->last = now;
57 unsigned after = ifa->cf->min_ra_int;
58 after += random() % (ifa->cf->max_ra_int - ifa->cf->min_ra_int + 1);
59
60 if (ifa->initial)
61 ifa->initial--;
62
63 if (ifa->initial)
64 after = MIN(after, MAX_INITIAL_RTR_ADVERT_INTERVAL);
65
66 tm_start(ifa->timer, after);
67}
68
69static char* ev_name[] = { NULL, "Init", "Change", "RS" };
70
71void
72radv_iface_notify(struct radv_iface *ifa, int event)
73{
74 struct proto_radv *ra = ifa->ra;
75
76 if (!ifa->sk)
77 return;
78
79 RADV_TRACE(D_EVENTS, "Event %s on %s", ev_name[event], ifa->iface->name);
80
81 switch (event)
82 {
83 case RA_EV_CHANGE:
84 ifa->plen = 0;
85 case RA_EV_INIT:
86 ifa->initial = MAX_INITIAL_RTR_ADVERTISEMENTS;
87 break;
88
89 case RA_EV_RS:
90 break;
91 }
92
93 /* Update timer */
94 unsigned delta = now - ifa->last;
95 unsigned after = 0;
96
97 if (delta < ifa->cf->min_delay)
98 after = ifa->cf->min_delay - delta;
99
100 tm_start(ifa->timer, after);
101}
102
36da2857
OZ
103static void
104radv_iface_notify_all(struct proto_radv *ra, int event)
105{
106 struct radv_iface *ifa;
107
108 WALK_LIST(ifa, ra->iface_list)
109 radv_iface_notify(ifa, event);
110}
111
112
93e868c7
OZ
113static struct radv_iface *
114radv_iface_find(struct proto_radv *ra, struct iface *what)
115{
116 struct radv_iface *ifa;
117
118 WALK_LIST(ifa, ra->iface_list)
119 if (ifa->iface == what)
120 return ifa;
121
122 return NULL;
123}
124
125static void
126radv_iface_add(struct object_lock *lock)
127{
128 struct radv_iface *ifa = lock->data;
129 struct proto_radv *ra = ifa->ra;
130
131 if (! radv_sk_open(ifa))
132 {
133 log(L_ERR "%s: Socket open failed on interface %s", ra->p.name, ifa->iface->name);
134 return;
135 }
136
137 radv_iface_notify(ifa, RA_EV_INIT);
138}
139
140static inline struct ifa *
141find_lladdr(struct iface *iface)
142{
143 struct ifa *a;
144 WALK_LIST(a, iface->addrs)
145 if (a->scope == SCOPE_LINK)
146 return a;
147
148 return NULL;
149}
150
151static void
152radv_iface_new(struct proto_radv *ra, struct iface *iface, struct radv_iface_config *cf)
153{
154 pool *pool = ra->p.pool;
155 struct radv_iface *ifa;
156
157 RADV_TRACE(D_EVENTS, "Adding interface %s", iface->name);
158
159 ifa = mb_allocz(pool, sizeof(struct radv_iface));
160 ifa->ra = ra;
161 ifa->cf = cf;
162 ifa->iface = iface;
163
164 add_tail(&ra->iface_list, NODE ifa);
165
166 ifa->addr = find_lladdr(iface);
167 if (!ifa->addr)
168 {
169 log(L_ERR "%s: Cannot find link-locad addr on interface %s", ra->p.name, iface->name);
170 return;
171 }
172
173 timer *tm = tm_new(pool);
174 tm->hook = radv_timer;
175 tm->data = ifa;
176 tm->randomize = 0;
177 tm->recurrent = 0;
178 ifa->timer = tm;
179
180 struct object_lock *lock = olock_new(pool);
181 lock->addr = IPA_NONE;
182 lock->type = OBJLOCK_IP;
183 lock->port = ICMPV6_PROTO;
184 lock->iface = iface;
185 lock->data = ifa;
186 lock->hook = radv_iface_add;
187 ifa->lock = lock;
188
189 olock_acquire(lock);
190}
191
192static void
193radv_iface_remove(struct radv_iface *ifa)
194{
195 struct proto_radv *ra = ifa->ra;
196 RADV_TRACE(D_EVENTS, "Removing interface %s", ifa->iface->name);
197
198 rem_node(NODE ifa);
199
200 rfree(ifa->sk);
201 rfree(ifa->timer);
202 rfree(ifa->lock);
203
204 mb_free(ifa);
205}
206
207static void
208radv_if_notify(struct proto *p, unsigned flags, struct iface *iface)
209{
210 struct proto_radv *ra = (struct proto_radv *) p;
211 struct radv_config *cf = (struct radv_config *) (p->cf);
212
213 if (iface->flags & IF_IGNORE)
214 return;
215
216 if (flags & IF_CHANGE_UP)
217 {
218 struct radv_iface_config *ic = (struct radv_iface_config *)
219 iface_patt_find(&cf->patt_list, iface, NULL);
220
221 if (ic)
222 radv_iface_new(ra, iface, ic);
223
224 return;
225 }
226
227 struct radv_iface *ifa = radv_iface_find(ra, iface);
228 if (!ifa)
229 return;
230
231 if (flags & IF_CHANGE_DOWN)
232 {
233 radv_iface_remove(ifa);
234 return;
235 }
236
237 if ((flags & IF_CHANGE_LINK) && (iface->flags & IF_LINK_UP))
238 radv_iface_notify(ifa, RA_EV_INIT);
239}
240
241static void
242radv_ifa_notify(struct proto *p, unsigned flags, struct ifa *a)
243{
244 struct proto_radv *ra = (struct proto_radv *) p;
245
246 if (a->flags & IA_SECONDARY)
247 return;
248
249 if (a->scope <= SCOPE_LINK)
250 return;
251
252 struct radv_iface *ifa = radv_iface_find(ra, a->iface);
253
254 if (ifa)
255 radv_iface_notify(ifa, RA_EV_CHANGE);
256}
257
36da2857
OZ
258static inline int radv_net_match_trigger(struct radv_config *cf, net *n)
259{
260 return cf->trigger_valid &&
261 (n->n.pxlen == cf->trigger_pxlen) &&
262 ipa_equal(n->n.prefix, cf->trigger_prefix);
263}
264
265int
266radv_import_control(struct proto *p, rte **new, ea_list **attrs UNUSED, struct linpool *pool UNUSED)
267{
268 // struct proto_radv *ra = (struct proto_radv *) p;
269 struct radv_config *cf = (struct radv_config *) (p->cf);
270
271 if (radv_net_match_trigger(cf, (*new)->net))
272 return RIC_PROCESS;
273
274 return RIC_DROP;
275}
276
277static void
278radv_rt_notify(struct proto *p, rtable *tbl UNUSED, net *n, rte *new, rte *old UNUSED, ea_list *attrs UNUSED)
279{
280 struct proto_radv *ra = (struct proto_radv *) p;
281 struct radv_config *cf = (struct radv_config *) (p->cf);
282
283 if (radv_net_match_trigger(cf, n))
284 {
285 u8 old_active = ra->active;
286 ra->active = !!new;
287
288 if (ra->active == old_active)
289 return;
290
291 if (ra->active)
292 RADV_TRACE(D_EVENTS, "Triggered");
293 else
294 RADV_TRACE(D_EVENTS, "Suppressed");
295
296 radv_iface_notify_all(ra, RA_EV_CHANGE);
297 }
298}
299
300static int
301radv_check_active(struct proto_radv *ra)
302{
303 struct radv_config *cf = (struct radv_config *) (ra->p.cf);
304
305 if (! cf->trigger_valid)
306 return 1;
307
308 return rt_examine(ra->p.table, cf->trigger_prefix, cf->trigger_pxlen,
309 &(ra->p), ra->p.cf->out_filter);
310}
311
93e868c7
OZ
312static struct proto *
313radv_init(struct proto_config *c)
314{
315 struct proto *p = proto_new(c, sizeof(struct proto_radv));
316
36da2857
OZ
317 p->accept_ra_types = RA_OPTIMAL;
318 p->import_control = radv_import_control;
319 p->rt_notify = radv_rt_notify;
93e868c7
OZ
320 p->if_notify = radv_if_notify;
321 p->ifa_notify = radv_ifa_notify;
322 return p;
323}
324
325static int
326radv_start(struct proto *p)
327{
328 struct proto_radv *ra = (struct proto_radv *) p;
36da2857 329 struct radv_config *cf = (struct radv_config *) (p->cf);
93e868c7
OZ
330
331 init_list(&(ra->iface_list));
36da2857 332 ra->active = !cf->trigger_valid;
93e868c7
OZ
333
334 return PS_UP;
335}
336
337static inline void
338radv_iface_shutdown(struct radv_iface *ifa)
339{
340 if (ifa->sk)
341 radv_send_ra(ifa, 1);
342}
343
344static int
345radv_shutdown(struct proto *p)
346{
347 struct proto_radv *ra = (struct proto_radv *) p;
348
349 struct radv_iface *ifa;
350 WALK_LIST(ifa, ra->iface_list)
351 radv_iface_shutdown(ifa);
352
353 return PS_DOWN;
354}
355
356static int
357radv_reconfigure(struct proto *p, struct proto_config *c)
358{
359 struct proto_radv *ra = (struct proto_radv *) p;
360 // struct radv_config *old = (struct radv_config *) (p->cf);
361 struct radv_config *new = (struct radv_config *) c;
362
363 /*
364 * The question is why there is a reconfigure function for RAdv if
365 * it has almost none internal state so restarting the protocol
366 * would probably suffice. One small reason is that restarting the
367 * protocol would lead to sending a RA with Router Lifetime 0
368 * causing nodes to temporary remove their default routes.
369 */
370
36da2857
OZ
371 p->cf = c; /* radv_check_active() requires proper p->cf */
372 ra->active = radv_check_active(ra);
373
93e868c7
OZ
374 struct iface *iface;
375 WALK_LIST(iface, iface_list)
376 {
377 struct radv_iface *ifa = radv_iface_find(ra, iface);
378 struct radv_iface_config *ic = (struct radv_iface_config *)
379 iface_patt_find(&new->patt_list, iface, NULL);
380
381 if (ifa && ic)
382 {
383 ifa->cf = ic;
384
385 /* We cheat here - always notify the change even if there isn't
386 any. That would leads just to a few unnecessary RAs. */
387 radv_iface_notify(ifa, RA_EV_CHANGE);
388 }
389
390 if (ifa && !ic)
391 {
392 radv_iface_shutdown(ifa);
393 radv_iface_remove(ifa);
394 }
395
396 if (!ifa && ic)
397 radv_iface_new(ra, iface, ic);
398 }
399
400 return 1;
401}
402
a7f23f58
OZ
403static void
404radv_copy_config(struct proto_config *dest, struct proto_config *src)
405{
406 struct radv_config *d = (struct radv_config *) dest;
407 struct radv_config *s = (struct radv_config *) src;
408
409 /* We clean up patt_list, ifaces are non-sharable */
410 init_list(&d->patt_list);
411
412 /* We copy pref_list, shallow copy suffices */
2779d50a 413 cfg_copy_list(&d->pref_list, &s->pref_list, sizeof(struct radv_prefix_config));
a7f23f58
OZ
414}
415
36da2857
OZ
416static void
417radv_get_status(struct proto *p, byte *buf)
418{
419 struct proto_radv *ra = (struct proto_radv *) p;
420
421 if (!ra->active)
422 strcpy(buf, "Suppressed");
423}
93e868c7
OZ
424
425struct protocol proto_radv = {
426 .name = "RAdv",
427 .template = "radv%d",
428 .init = radv_init,
429 .start = radv_start,
430 .shutdown = radv_shutdown,
a7f23f58 431 .reconfigure = radv_reconfigure,
36da2857
OZ
432 .copy_config = radv_copy_config,
433 .get_status = radv_get_status
93e868c7 434};