]> git.ipfire.org Git - thirdparty/bird.git/blame - proto/radv/radv.c
Unit Testing for BIRD
[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).
1ec52253 18 * The protocol does not export any routes.
93e868c7
OZ
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)
75148289 43 * - RFC 4191 (partial) - Default Router Preference
93e868c7
OZ
44 */
45
46static void
47radv_timer(timer *tm)
48{
49 struct radv_iface *ifa = tm->data;
50 struct proto_radv *ra = ifa->ra;
51
52 RADV_TRACE(D_EVENTS, "Timer fired on %s", ifa->iface->name);
53
54 radv_send_ra(ifa, 0);
55
56 /* Update timer */
57 ifa->last = now;
58 unsigned after = ifa->cf->min_ra_int;
59 after += random() % (ifa->cf->max_ra_int - ifa->cf->min_ra_int + 1);
60
61 if (ifa->initial)
62 ifa->initial--;
63
64 if (ifa->initial)
65 after = MIN(after, MAX_INITIAL_RTR_ADVERT_INTERVAL);
66
67 tm_start(ifa->timer, after);
68}
69
70static char* ev_name[] = { NULL, "Init", "Change", "RS" };
71
72void
73radv_iface_notify(struct radv_iface *ifa, int event)
74{
75 struct proto_radv *ra = ifa->ra;
76
77 if (!ifa->sk)
78 return;
79
80 RADV_TRACE(D_EVENTS, "Event %s on %s", ev_name[event], ifa->iface->name);
81
82 switch (event)
83 {
84 case RA_EV_CHANGE:
85 ifa->plen = 0;
86 case RA_EV_INIT:
87 ifa->initial = MAX_INITIAL_RTR_ADVERTISEMENTS;
88 break;
89
90 case RA_EV_RS:
91 break;
92 }
93
94 /* Update timer */
95 unsigned delta = now - ifa->last;
96 unsigned after = 0;
97
98 if (delta < ifa->cf->min_delay)
99 after = ifa->cf->min_delay - delta;
100
101 tm_start(ifa->timer, after);
102}
103
36da2857
OZ
104static void
105radv_iface_notify_all(struct proto_radv *ra, int event)
106{
107 struct radv_iface *ifa;
108
109 WALK_LIST(ifa, ra->iface_list)
110 radv_iface_notify(ifa, event);
111}
112
113
93e868c7
OZ
114static struct radv_iface *
115radv_iface_find(struct proto_radv *ra, struct iface *what)
116{
117 struct radv_iface *ifa;
118
119 WALK_LIST(ifa, ra->iface_list)
120 if (ifa->iface == what)
121 return ifa;
122
123 return NULL;
124}
125
126static void
127radv_iface_add(struct object_lock *lock)
128{
129 struct radv_iface *ifa = lock->data;
130 struct proto_radv *ra = ifa->ra;
131
132 if (! radv_sk_open(ifa))
133 {
134 log(L_ERR "%s: Socket open failed on interface %s", ra->p.name, ifa->iface->name);
135 return;
136 }
137
138 radv_iface_notify(ifa, RA_EV_INIT);
139}
140
141static inline struct ifa *
142find_lladdr(struct iface *iface)
143{
144 struct ifa *a;
145 WALK_LIST(a, iface->addrs)
d44e686e 146 if ((a->prefix.type == NET_IP6) && (a->scope == SCOPE_LINK))
93e868c7
OZ
147 return a;
148
149 return NULL;
150}
151
152static void
153radv_iface_new(struct proto_radv *ra, struct iface *iface, struct radv_iface_config *cf)
154{
155 pool *pool = ra->p.pool;
156 struct radv_iface *ifa;
157
158 RADV_TRACE(D_EVENTS, "Adding interface %s", iface->name);
159
160 ifa = mb_allocz(pool, sizeof(struct radv_iface));
161 ifa->ra = ra;
162 ifa->cf = cf;
163 ifa->iface = iface;
164
165 add_tail(&ra->iface_list, NODE ifa);
166
167 ifa->addr = find_lladdr(iface);
168 if (!ifa->addr)
169 {
170 log(L_ERR "%s: Cannot find link-locad addr on interface %s", ra->p.name, iface->name);
171 return;
172 }
173
174 timer *tm = tm_new(pool);
175 tm->hook = radv_timer;
176 tm->data = ifa;
177 tm->randomize = 0;
178 tm->recurrent = 0;
179 ifa->timer = tm;
180
181 struct object_lock *lock = olock_new(pool);
182 lock->addr = IPA_NONE;
183 lock->type = OBJLOCK_IP;
184 lock->port = ICMPV6_PROTO;
185 lock->iface = iface;
186 lock->data = ifa;
187 lock->hook = radv_iface_add;
188 ifa->lock = lock;
189
190 olock_acquire(lock);
191}
192
193static void
194radv_iface_remove(struct radv_iface *ifa)
195{
196 struct proto_radv *ra = ifa->ra;
197 RADV_TRACE(D_EVENTS, "Removing interface %s", ifa->iface->name);
198
199 rem_node(NODE ifa);
200
201 rfree(ifa->sk);
202 rfree(ifa->timer);
203 rfree(ifa->lock);
204
205 mb_free(ifa);
206}
207
208static void
209radv_if_notify(struct proto *p, unsigned flags, struct iface *iface)
2bbc3083 210{
93e868c7
OZ
211 struct proto_radv *ra = (struct proto_radv *) p;
212 struct radv_config *cf = (struct radv_config *) (p->cf);
213
214 if (iface->flags & IF_IGNORE)
215 return;
216
217 if (flags & IF_CHANGE_UP)
218 {
219 struct radv_iface_config *ic = (struct radv_iface_config *)
220 iface_patt_find(&cf->patt_list, iface, NULL);
221
222 if (ic)
223 radv_iface_new(ra, iface, ic);
224
225 return;
226 }
227
228 struct radv_iface *ifa = radv_iface_find(ra, iface);
229 if (!ifa)
230 return;
231
232 if (flags & IF_CHANGE_DOWN)
233 {
234 radv_iface_remove(ifa);
235 return;
236 }
237
238 if ((flags & IF_CHANGE_LINK) && (iface->flags & IF_LINK_UP))
239 radv_iface_notify(ifa, RA_EV_INIT);
240}
241
242static void
3e236955 243radv_ifa_notify(struct proto *p, unsigned flags UNUSED, struct ifa *a)
93e868c7
OZ
244{
245 struct proto_radv *ra = (struct proto_radv *) p;
246
247 if (a->flags & IA_SECONDARY)
248 return;
249
250 if (a->scope <= SCOPE_LINK)
251 return;
252
253 struct radv_iface *ifa = radv_iface_find(ra, a->iface);
254
255 if (ifa)
256 radv_iface_notify(ifa, RA_EV_CHANGE);
257}
258
f4a60a9b
OZ
259static inline int
260radv_trigger_valid(struct radv_config *cf)
36da2857 261{
f4a60a9b
OZ
262 return cf->trigger.type != 0;
263}
264
265static inline int
266radv_net_match_trigger(struct radv_config *cf, net *n)
267{
268 return radv_trigger_valid(cf) && net_equal(n->n.addr, &cf->trigger);
36da2857
OZ
269}
270
271int
272radv_import_control(struct proto *p, rte **new, ea_list **attrs UNUSED, struct linpool *pool UNUSED)
273{
274 // struct proto_radv *ra = (struct proto_radv *) p;
275 struct radv_config *cf = (struct radv_config *) (p->cf);
276
277 if (radv_net_match_trigger(cf, (*new)->net))
278 return RIC_PROCESS;
279
280 return RIC_DROP;
281}
282
283static void
4bdf1881 284radv_rt_notify(struct proto *p, struct channel *ch UNUSED, net *n, rte *new, rte *old UNUSED, ea_list *attrs UNUSED)
36da2857
OZ
285{
286 struct proto_radv *ra = (struct proto_radv *) p;
287 struct radv_config *cf = (struct radv_config *) (p->cf);
288
289 if (radv_net_match_trigger(cf, n))
290 {
291 u8 old_active = ra->active;
292 ra->active = !!new;
293
294 if (ra->active == old_active)
295 return;
296
297 if (ra->active)
298 RADV_TRACE(D_EVENTS, "Triggered");
299 else
300 RADV_TRACE(D_EVENTS, "Suppressed");
301
302 radv_iface_notify_all(ra, RA_EV_CHANGE);
303 }
304}
305
306static int
307radv_check_active(struct proto_radv *ra)
308{
309 struct radv_config *cf = (struct radv_config *) (ra->p.cf);
310
f4a60a9b 311 if (!radv_trigger_valid(cf))
36da2857
OZ
312 return 1;
313
9b0a0ba9 314 struct channel *c = ra->p.main_channel;
f4a60a9b
OZ
315 return rt_examine(c->table, &cf->trigger, &ra->p, c->out_filter);
316}
317
318static void
319radv_postconfig(struct proto_config *CF)
320{
321 // struct radv_config *cf = (void *) CF;
322
323 /* Define default channel */
324 if (EMPTY_LIST(CF->channels))
325 channel_config_new(NULL, NET_IP6, CF);
36da2857
OZ
326}
327
93e868c7 328static struct proto *
f4a60a9b 329radv_init(struct proto_config *CF)
93e868c7 330{
f4a60a9b
OZ
331 struct proto *p = proto_new(CF);
332
333 p->main_channel = proto_add_channel(p, proto_cf_main_channel(CF));
93e868c7 334
36da2857
OZ
335 p->import_control = radv_import_control;
336 p->rt_notify = radv_rt_notify;
93e868c7
OZ
337 p->if_notify = radv_if_notify;
338 p->ifa_notify = radv_ifa_notify;
f4a60a9b 339
93e868c7
OZ
340 return p;
341}
342
343static int
344radv_start(struct proto *p)
345{
346 struct proto_radv *ra = (struct proto_radv *) p;
36da2857 347 struct radv_config *cf = (struct radv_config *) (p->cf);
93e868c7
OZ
348
349 init_list(&(ra->iface_list));
f4a60a9b 350 ra->active = !radv_trigger_valid(cf);
93e868c7
OZ
351
352 return PS_UP;
353}
354
355static inline void
356radv_iface_shutdown(struct radv_iface *ifa)
357{
358 if (ifa->sk)
359 radv_send_ra(ifa, 1);
360}
361
362static int
363radv_shutdown(struct proto *p)
364{
365 struct proto_radv *ra = (struct proto_radv *) p;
366
367 struct radv_iface *ifa;
368 WALK_LIST(ifa, ra->iface_list)
369 radv_iface_shutdown(ifa);
370
371 return PS_DOWN;
372}
373
374static int
f4a60a9b 375radv_reconfigure(struct proto *p, struct proto_config *CF)
93e868c7
OZ
376{
377 struct proto_radv *ra = (struct proto_radv *) p;
378 // struct radv_config *old = (struct radv_config *) (p->cf);
f4a60a9b 379 struct radv_config *new = (struct radv_config *) CF;
93e868c7 380
2bbc3083 381 /*
93e868c7
OZ
382 * The question is why there is a reconfigure function for RAdv if
383 * it has almost none internal state so restarting the protocol
384 * would probably suffice. One small reason is that restarting the
385 * protocol would lead to sending a RA with Router Lifetime 0
386 * causing nodes to temporary remove their default routes.
387 */
388
f4a60a9b
OZ
389 if (!proto_configure_channel(p, &p->main_channel, proto_cf_main_channel(CF)))
390 return 0;
391
392 p->cf = CF; /* radv_check_active() requires proper p->cf */
36da2857
OZ
393 ra->active = radv_check_active(ra);
394
93e868c7
OZ
395 struct iface *iface;
396 WALK_LIST(iface, iface_list)
397 {
398 struct radv_iface *ifa = radv_iface_find(ra, iface);
399 struct radv_iface_config *ic = (struct radv_iface_config *)
400 iface_patt_find(&new->patt_list, iface, NULL);
401
402 if (ifa && ic)
403 {
404 ifa->cf = ic;
405
406 /* We cheat here - always notify the change even if there isn't
407 any. That would leads just to a few unnecessary RAs. */
408 radv_iface_notify(ifa, RA_EV_CHANGE);
409 }
410
411 if (ifa && !ic)
412 {
413 radv_iface_shutdown(ifa);
414 radv_iface_remove(ifa);
415 }
416
417 if (!ifa && ic)
418 radv_iface_new(ra, iface, ic);
419 }
420
421 return 1;
422}
423
a7f23f58
OZ
424static void
425radv_copy_config(struct proto_config *dest, struct proto_config *src)
426{
427 struct radv_config *d = (struct radv_config *) dest;
428 struct radv_config *s = (struct radv_config *) src;
429
430 /* We clean up patt_list, ifaces are non-sharable */
431 init_list(&d->patt_list);
432
433 /* We copy pref_list, shallow copy suffices */
2779d50a 434 cfg_copy_list(&d->pref_list, &s->pref_list, sizeof(struct radv_prefix_config));
a7f23f58
OZ
435}
436
36da2857
OZ
437static void
438radv_get_status(struct proto *p, byte *buf)
439{
440 struct proto_radv *ra = (struct proto_radv *) p;
441
442 if (!ra->active)
443 strcpy(buf, "Suppressed");
444}
93e868c7
OZ
445
446struct protocol proto_radv = {
447 .name = "RAdv",
448 .template = "radv%d",
f4a60a9b
OZ
449 .channel_mask = NB_IP6,
450 .proto_size = sizeof(struct proto_radv),
2bbc3083 451 .config_size = sizeof(struct radv_config),
f4a60a9b 452 .postconfig = radv_postconfig,
93e868c7
OZ
453 .init = radv_init,
454 .start = radv_start,
455 .shutdown = radv_shutdown,
a7f23f58 456 .reconfigure = radv_reconfigure,
36da2857
OZ
457 .copy_config = radv_copy_config,
458 .get_status = radv_get_status
93e868c7 459};