]>
Commit | Line | Data |
---|---|---|
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 | ||
46 | static void | |
47 | radv_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 | ||
70 | static char* ev_name[] = { NULL, "Init", "Change", "RS" }; | |
71 | ||
72 | void | |
73 | radv_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 |
104 | static void |
105 | radv_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 |
114 | static struct radv_iface * |
115 | radv_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 | ||
126 | static void | |
127 | radv_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 | ||
141 | static inline struct ifa * | |
142 | find_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 | ||
152 | static void | |
153 | radv_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 | ||
193 | static void | |
194 | radv_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 | ||
208 | static void | |
209 | radv_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 | ||
242 | static void | |
3e236955 | 243 | radv_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 |
259 | static inline int |
260 | radv_trigger_valid(struct radv_config *cf) | |
36da2857 | 261 | { |
f4a60a9b OZ |
262 | return cf->trigger.type != 0; |
263 | } | |
264 | ||
265 | static inline int | |
266 | radv_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 | ||
271 | int | |
272 | radv_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 | ||
283 | static void | |
4bdf1881 | 284 | radv_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 | ||
306 | static int | |
307 | radv_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 | ||
318 | static void | |
319 | radv_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 | 328 | static struct proto * |
f4a60a9b | 329 | radv_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 | ||
343 | static int | |
344 | radv_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 | ||
355 | static inline void | |
356 | radv_iface_shutdown(struct radv_iface *ifa) | |
357 | { | |
358 | if (ifa->sk) | |
359 | radv_send_ra(ifa, 1); | |
360 | } | |
361 | ||
362 | static int | |
363 | radv_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 | ||
374 | static int | |
f4a60a9b | 375 | radv_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 |
424 | static void |
425 | radv_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 |
437 | static void |
438 | radv_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 | |
446 | struct 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 | }; |