]> git.ipfire.org Git - thirdparty/bird.git/blob - proto/radv/radv.c
Merge branch 'rt-accepted'
[thirdparty/bird.git] / proto / radv / radv.c
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.
32 *
33 * Supported standards:
34 * - RFC 4861 - main RA standard
35 * - RFC 6106 - DNS extensions (RDDNS, DNSSL)
36 */
37
38 static void
39 radv_timer(timer *tm)
40 {
41 struct radv_iface *ifa = tm->data;
42 struct proto_radv *ra = ifa->ra;
43
44 RADV_TRACE(D_EVENTS, "Timer fired on %s", ifa->iface->name);
45
46 radv_send_ra(ifa, 0);
47
48 /* Update timer */
49 ifa->last = now;
50 unsigned after = ifa->cf->min_ra_int;
51 after += random() % (ifa->cf->max_ra_int - ifa->cf->min_ra_int + 1);
52
53 if (ifa->initial)
54 ifa->initial--;
55
56 if (ifa->initial)
57 after = MIN(after, MAX_INITIAL_RTR_ADVERT_INTERVAL);
58
59 tm_start(ifa->timer, after);
60 }
61
62 static char* ev_name[] = { NULL, "Init", "Change", "RS" };
63
64 void
65 radv_iface_notify(struct radv_iface *ifa, int event)
66 {
67 struct proto_radv *ra = ifa->ra;
68
69 if (!ifa->sk)
70 return;
71
72 RADV_TRACE(D_EVENTS, "Event %s on %s", ev_name[event], ifa->iface->name);
73
74 switch (event)
75 {
76 case RA_EV_CHANGE:
77 ifa->plen = 0;
78 case RA_EV_INIT:
79 ifa->initial = MAX_INITIAL_RTR_ADVERTISEMENTS;
80 break;
81
82 case RA_EV_RS:
83 break;
84 }
85
86 /* Update timer */
87 unsigned delta = now - ifa->last;
88 unsigned after = 0;
89
90 if (delta < ifa->cf->min_delay)
91 after = ifa->cf->min_delay - delta;
92
93 tm_start(ifa->timer, after);
94 }
95
96 static struct radv_iface *
97 radv_iface_find(struct proto_radv *ra, struct iface *what)
98 {
99 struct radv_iface *ifa;
100
101 WALK_LIST(ifa, ra->iface_list)
102 if (ifa->iface == what)
103 return ifa;
104
105 return NULL;
106 }
107
108 static void
109 radv_iface_add(struct object_lock *lock)
110 {
111 struct radv_iface *ifa = lock->data;
112 struct proto_radv *ra = ifa->ra;
113
114 if (! radv_sk_open(ifa))
115 {
116 log(L_ERR "%s: Socket open failed on interface %s", ra->p.name, ifa->iface->name);
117 return;
118 }
119
120 radv_iface_notify(ifa, RA_EV_INIT);
121 }
122
123 static inline struct ifa *
124 find_lladdr(struct iface *iface)
125 {
126 struct ifa *a;
127 WALK_LIST(a, iface->addrs)
128 if (a->scope == SCOPE_LINK)
129 return a;
130
131 return NULL;
132 }
133
134 static void
135 radv_iface_new(struct proto_radv *ra, struct iface *iface, struct radv_iface_config *cf)
136 {
137 pool *pool = ra->p.pool;
138 struct radv_iface *ifa;
139
140 RADV_TRACE(D_EVENTS, "Adding interface %s", iface->name);
141
142 ifa = mb_allocz(pool, sizeof(struct radv_iface));
143 ifa->ra = ra;
144 ifa->cf = cf;
145 ifa->iface = iface;
146
147 add_tail(&ra->iface_list, NODE ifa);
148
149 ifa->addr = find_lladdr(iface);
150 if (!ifa->addr)
151 {
152 log(L_ERR "%s: Cannot find link-locad addr on interface %s", ra->p.name, iface->name);
153 return;
154 }
155
156 timer *tm = tm_new(pool);
157 tm->hook = radv_timer;
158 tm->data = ifa;
159 tm->randomize = 0;
160 tm->recurrent = 0;
161 ifa->timer = tm;
162
163 struct object_lock *lock = olock_new(pool);
164 lock->addr = IPA_NONE;
165 lock->type = OBJLOCK_IP;
166 lock->port = ICMPV6_PROTO;
167 lock->iface = iface;
168 lock->data = ifa;
169 lock->hook = radv_iface_add;
170 ifa->lock = lock;
171
172 olock_acquire(lock);
173 }
174
175 static void
176 radv_iface_remove(struct radv_iface *ifa)
177 {
178 struct proto_radv *ra = ifa->ra;
179 RADV_TRACE(D_EVENTS, "Removing interface %s", ifa->iface->name);
180
181 rem_node(NODE ifa);
182
183 rfree(ifa->sk);
184 rfree(ifa->timer);
185 rfree(ifa->lock);
186
187 mb_free(ifa);
188 }
189
190 static void
191 radv_if_notify(struct proto *p, unsigned flags, struct iface *iface)
192 {
193 struct proto_radv *ra = (struct proto_radv *) p;
194 struct radv_config *cf = (struct radv_config *) (p->cf);
195
196 if (iface->flags & IF_IGNORE)
197 return;
198
199 if (flags & IF_CHANGE_UP)
200 {
201 struct radv_iface_config *ic = (struct radv_iface_config *)
202 iface_patt_find(&cf->patt_list, iface, NULL);
203
204 if (ic)
205 radv_iface_new(ra, iface, ic);
206
207 return;
208 }
209
210 struct radv_iface *ifa = radv_iface_find(ra, iface);
211 if (!ifa)
212 return;
213
214 if (flags & IF_CHANGE_DOWN)
215 {
216 radv_iface_remove(ifa);
217 return;
218 }
219
220 if ((flags & IF_CHANGE_LINK) && (iface->flags & IF_LINK_UP))
221 radv_iface_notify(ifa, RA_EV_INIT);
222 }
223
224 static void
225 radv_ifa_notify(struct proto *p, unsigned flags, struct ifa *a)
226 {
227 struct proto_radv *ra = (struct proto_radv *) p;
228
229 if (a->flags & IA_SECONDARY)
230 return;
231
232 if (a->scope <= SCOPE_LINK)
233 return;
234
235 struct radv_iface *ifa = radv_iface_find(ra, a->iface);
236
237 if (ifa)
238 radv_iface_notify(ifa, RA_EV_CHANGE);
239 }
240
241 static struct proto *
242 radv_init(struct proto_config *c)
243 {
244 struct proto *p = proto_new(c, sizeof(struct proto_radv));
245
246 p->if_notify = radv_if_notify;
247 p->ifa_notify = radv_ifa_notify;
248 return p;
249 }
250
251 static int
252 radv_start(struct proto *p)
253 {
254 struct proto_radv *ra = (struct proto_radv *) p;
255 // struct radv_config *cf = (struct radv_config *) (p->cf);
256
257 init_list(&(ra->iface_list));
258
259 return PS_UP;
260 }
261
262 static inline void
263 radv_iface_shutdown(struct radv_iface *ifa)
264 {
265 if (ifa->sk)
266 radv_send_ra(ifa, 1);
267 }
268
269 static int
270 radv_shutdown(struct proto *p)
271 {
272 struct proto_radv *ra = (struct proto_radv *) p;
273
274 struct radv_iface *ifa;
275 WALK_LIST(ifa, ra->iface_list)
276 radv_iface_shutdown(ifa);
277
278 return PS_DOWN;
279 }
280
281 static int
282 radv_reconfigure(struct proto *p, struct proto_config *c)
283 {
284 struct proto_radv *ra = (struct proto_radv *) p;
285 // struct radv_config *old = (struct radv_config *) (p->cf);
286 struct radv_config *new = (struct radv_config *) c;
287
288 /*
289 * The question is why there is a reconfigure function for RAdv if
290 * it has almost none internal state so restarting the protocol
291 * would probably suffice. One small reason is that restarting the
292 * protocol would lead to sending a RA with Router Lifetime 0
293 * causing nodes to temporary remove their default routes.
294 */
295
296 struct iface *iface;
297 WALK_LIST(iface, iface_list)
298 {
299 struct radv_iface *ifa = radv_iface_find(ra, iface);
300 struct radv_iface_config *ic = (struct radv_iface_config *)
301 iface_patt_find(&new->patt_list, iface, NULL);
302
303 if (ifa && ic)
304 {
305 ifa->cf = ic;
306
307 /* We cheat here - always notify the change even if there isn't
308 any. That would leads just to a few unnecessary RAs. */
309 radv_iface_notify(ifa, RA_EV_CHANGE);
310 }
311
312 if (ifa && !ic)
313 {
314 radv_iface_shutdown(ifa);
315 radv_iface_remove(ifa);
316 }
317
318 if (!ifa && ic)
319 radv_iface_new(ra, iface, ic);
320 }
321
322 return 1;
323 }
324
325 static void
326 radv_copy_config(struct proto_config *dest, struct proto_config *src)
327 {
328 struct radv_config *d = (struct radv_config *) dest;
329 struct radv_config *s = (struct radv_config *) src;
330
331 /* We clean up patt_list, ifaces are non-sharable */
332 init_list(&d->patt_list);
333
334 /* We copy pref_list, shallow copy suffices */
335 cfg_copy_list(&d->pref_list, &s->pref_list, sizeof(struct radv_prefix_config));
336 }
337
338
339 struct protocol proto_radv = {
340 .name = "RAdv",
341 .template = "radv%d",
342 .init = radv_init,
343 .start = radv_start,
344 .shutdown = radv_shutdown,
345 .reconfigure = radv_reconfigure,
346 .copy_config = radv_copy_config
347 };