]>
Commit | Line | Data |
---|---|---|
93e868c7 OZ |
1 | /* |
2 | * BIRD -- RAdv Packet Processing | |
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 | struct radv_ra_packet | |
13 | { | |
14 | u8 type; | |
15 | u8 code; | |
16 | u16 checksum; | |
17 | u8 current_hop_limit; | |
18 | u8 flags; | |
19 | u16 router_lifetime; | |
20 | u32 reachable_time; | |
21 | u32 retrans_timer; | |
22 | }; | |
23 | ||
24 | #define OPT_RA_MANAGED 0x80 | |
25 | #define OPT_RA_OTHER_CFG 0x40 | |
26 | ||
fc06fb62 OZ |
27 | #define OPT_PREFIX 3 |
28 | #define OPT_MTU 5 | |
29 | #define OPT_RDNSS 25 | |
30 | #define OPT_DNSSL 31 | |
93e868c7 OZ |
31 | |
32 | struct radv_opt_prefix | |
33 | { | |
34 | u8 type; | |
35 | u8 length; | |
36 | u8 pxlen; | |
37 | u8 flags; | |
38 | u32 valid_lifetime; | |
39 | u32 preferred_lifetime; | |
40 | u32 reserved; | |
41 | ip_addr prefix; | |
42 | }; | |
43 | ||
44 | #define OPT_PX_ONLINK 0x80 | |
45 | #define OPT_PX_AUTONOMOUS 0x40 | |
46 | ||
47 | struct radv_opt_mtu | |
48 | { | |
49 | u8 type; | |
50 | u8 length; | |
51 | u16 reserved; | |
52 | u32 mtu; | |
53 | }; | |
54 | ||
fc06fb62 OZ |
55 | struct radv_opt_rdnss |
56 | { | |
57 | u8 type; | |
58 | u8 length; | |
59 | u16 reserved; | |
60 | u32 lifetime; | |
61 | ip_addr servers[]; | |
62 | }; | |
63 | ||
64 | struct radv_opt_dnssl | |
65 | { | |
66 | u8 type; | |
67 | u8 length; | |
68 | u16 reserved; | |
69 | u32 lifetime; | |
70 | char domain[]; | |
71 | }; | |
72 | ||
73 | ||
93e868c7 OZ |
74 | static struct radv_prefix_config default_prefix = { |
75 | .onlink = 1, | |
76 | .autonomous = 1, | |
77 | .valid_lifetime = DEFAULT_VALID_LIFETIME, | |
78 | .preferred_lifetime = DEFAULT_PREFERRED_LIFETIME | |
79 | }; | |
80 | ||
fc06fb62 | 81 | |
93e868c7 OZ |
82 | static struct radv_prefix_config * |
83 | radv_prefix_match(struct radv_iface *ifa, struct ifa *a) | |
84 | { | |
85 | struct proto *p = &ifa->ra->p; | |
86 | struct radv_config *cf = (struct radv_config *) (p->cf); | |
87 | struct radv_prefix_config *pc; | |
88 | ||
89 | if (a->scope <= SCOPE_LINK) | |
90 | return NULL; | |
91 | ||
92 | WALK_LIST(pc, ifa->cf->pref_list) | |
93 | if ((a->pxlen >= pc->pxlen) && ipa_in_net(a->prefix, pc->prefix, pc->pxlen)) | |
94 | return pc; | |
95 | ||
96 | WALK_LIST(pc, cf->pref_list) | |
97 | if ((a->pxlen >= pc->pxlen) && ipa_in_net(a->prefix, pc->prefix, pc->pxlen)) | |
98 | return pc; | |
99 | ||
100 | return &default_prefix; | |
101 | } | |
102 | ||
fc06fb62 OZ |
103 | static int |
104 | radv_prepare_rdnss(struct radv_iface *ifa, list *rdnss_list, char **buf, char *bufend) | |
105 | { | |
106 | struct radv_rdnss_config *rcf = HEAD(*rdnss_list); | |
107 | ||
108 | while(NODE_VALID(rcf)) | |
109 | { | |
110 | struct radv_rdnss_config *rcf_base = rcf; | |
111 | struct radv_opt_rdnss *op = (void *) *buf; | |
112 | int max_i = (bufend - *buf - sizeof(struct radv_opt_rdnss)) / sizeof(ip_addr); | |
113 | int i = 0; | |
114 | ||
115 | if (max_i < 1) | |
116 | goto too_much; | |
117 | ||
118 | op->type = OPT_RDNSS; | |
119 | op->reserved = 0; | |
120 | ||
121 | if (rcf->lifetime_mult) | |
122 | op->lifetime = htonl(rcf->lifetime_mult * ifa->cf->max_ra_int); | |
123 | else | |
124 | op->lifetime = htonl(rcf->lifetime); | |
125 | ||
126 | while(NODE_VALID(rcf) && | |
127 | (rcf->lifetime == rcf_base->lifetime) && | |
128 | (rcf->lifetime_mult == rcf_base->lifetime_mult)) | |
129 | { | |
130 | if (i >= max_i) | |
131 | goto too_much; | |
132 | ||
133 | op->servers[i] = rcf->server; | |
134 | ipa_hton(op->servers[i]); | |
135 | i++; | |
136 | ||
137 | rcf = NODE_NEXT(rcf); | |
138 | } | |
139 | ||
140 | op->length = 1+2*i; | |
141 | *buf += 8 * op->length; | |
142 | } | |
143 | ||
144 | return 0; | |
145 | ||
146 | too_much: | |
147 | log(L_WARN "%s: Too many RA options on interface %s", | |
148 | ifa->ra->p.name, ifa->iface->name); | |
149 | return -1; | |
150 | } | |
151 | ||
152 | int | |
153 | radv_process_domain(struct radv_dnssl_config *cf) | |
154 | { | |
155 | /* Format of domain in search list is <size> <label> <size> <label> ... 0 */ | |
156 | ||
157 | char *dom = cf->domain; | |
158 | char *dom_end = dom; /* Just to */ | |
159 | u8 *dlen_save = &cf->dlen_first; | |
160 | int len; | |
161 | ||
162 | while (dom_end) | |
163 | { | |
164 | dom_end = strchr(dom, '.'); | |
165 | len = dom_end ? (dom_end - dom) : strlen(dom); | |
166 | ||
167 | if (len < 1 || len > 63) | |
168 | return -1; | |
169 | ||
170 | *dlen_save = len; | |
171 | dlen_save = (u8 *) dom_end; | |
172 | ||
173 | dom += len + 1; | |
174 | } | |
175 | ||
176 | len = dom - cf->domain; | |
177 | if (len > 254) | |
178 | return -1; | |
179 | ||
180 | cf->dlen_all = len; | |
181 | ||
182 | return 0; | |
183 | } | |
184 | ||
185 | static int | |
186 | radv_prepare_dnssl(struct radv_iface *ifa, list *dnssl_list, char **buf, char *bufend) | |
187 | { | |
188 | struct radv_dnssl_config *dcf = HEAD(*dnssl_list); | |
189 | ||
190 | while(NODE_VALID(dcf)) | |
191 | { | |
192 | struct radv_dnssl_config *dcf_base = dcf; | |
193 | struct radv_opt_dnssl *op = (void *) *buf; | |
194 | int bsize = bufend - *buf - sizeof(struct radv_opt_dnssl); | |
195 | int bpos = 0; | |
196 | ||
197 | if (bsize < 0) | |
198 | goto too_much; | |
199 | ||
200 | bsize = bsize & ~7; /* Round down to multiples of 8 */ | |
201 | ||
202 | op->type = OPT_DNSSL; | |
203 | op->reserved = 0; | |
204 | ||
205 | if (dcf->lifetime_mult) | |
206 | op->lifetime = htonl(dcf->lifetime_mult * ifa->cf->max_ra_int); | |
207 | else | |
208 | op->lifetime = htonl(dcf->lifetime); | |
209 | ||
210 | while(NODE_VALID(dcf) && | |
211 | (dcf->lifetime == dcf_base->lifetime) && | |
212 | (dcf->lifetime_mult == dcf_base->lifetime_mult)) | |
213 | { | |
214 | if (bpos + dcf->dlen_all + 1 > bsize) | |
215 | goto too_much; | |
216 | ||
217 | op->domain[bpos++] = dcf->dlen_first; | |
218 | memcpy(op->domain + bpos, dcf->domain, dcf->dlen_all); | |
219 | bpos += dcf->dlen_all; | |
220 | ||
221 | dcf = NODE_NEXT(dcf); | |
222 | } | |
223 | ||
224 | int blen = (bpos + 7) / 8; | |
225 | bzero(op->domain + bpos, 8 * blen - bpos); | |
226 | op->length = 1 + blen; | |
227 | *buf += 8 * op->length; | |
228 | } | |
229 | ||
230 | return 0; | |
231 | ||
232 | too_much: | |
233 | log(L_WARN "%s: Too many RA options on interface %s", | |
234 | ifa->ra->p.name, ifa->iface->name); | |
235 | return -1; | |
236 | } | |
237 | ||
93e868c7 OZ |
238 | static void |
239 | radv_prepare_ra(struct radv_iface *ifa) | |
240 | { | |
241 | struct proto_radv *ra = ifa->ra; | |
fc06fb62 | 242 | struct radv_config *cf = (struct radv_config *) (ra->p.cf); |
36da2857 | 243 | struct radv_iface_config *ic = ifa->cf; |
93e868c7 OZ |
244 | |
245 | char *buf = ifa->sk->tbuf; | |
246 | char *bufstart = buf; | |
247 | char *bufend = buf + ifa->sk->tbsize; | |
248 | ||
249 | struct radv_ra_packet *pkt = (void *) buf; | |
250 | pkt->type = ICMPV6_RA; | |
251 | pkt->code = 0; | |
252 | pkt->checksum = 0; | |
36da2857 OZ |
253 | pkt->current_hop_limit = ic->current_hop_limit; |
254 | pkt->flags = (ic->managed ? OPT_RA_MANAGED : 0) | | |
255 | (ic->other_config ? OPT_RA_OTHER_CFG : 0); | |
256 | pkt->router_lifetime = (ra->active || !ic->default_lifetime_sensitive) ? | |
257 | htons(ic->default_lifetime) : 0; | |
258 | pkt->reachable_time = htonl(ic->reachable_time); | |
259 | pkt->retrans_timer = htonl(ic->retrans_timer); | |
93e868c7 OZ |
260 | buf += sizeof(*pkt); |
261 | ||
36da2857 | 262 | if (ic->link_mtu) |
93e868c7 OZ |
263 | { |
264 | struct radv_opt_mtu *om = (void *) buf; | |
265 | om->type = OPT_MTU; | |
266 | om->length = 1; | |
267 | om->reserved = 0; | |
36da2857 | 268 | om->mtu = htonl(ic->link_mtu); |
93e868c7 OZ |
269 | buf += sizeof (*om); |
270 | } | |
271 | ||
272 | struct ifa *addr; | |
273 | WALK_LIST(addr, ifa->iface->addrs) | |
274 | { | |
275 | struct radv_prefix_config *pc; | |
276 | pc = radv_prefix_match(ifa, addr); | |
277 | ||
278 | if (!pc || pc->skip) | |
279 | continue; | |
280 | ||
281 | if (buf + sizeof(struct radv_opt_prefix) > bufend) | |
282 | { | |
283 | log(L_WARN "%s: Too many prefixes on interface %s", ra->p.name, ifa->iface->name); | |
fc06fb62 | 284 | goto done; |
93e868c7 OZ |
285 | } |
286 | ||
287 | struct radv_opt_prefix *op = (void *) buf; | |
288 | op->type = OPT_PREFIX; | |
289 | op->length = 4; | |
290 | op->pxlen = addr->pxlen; | |
291 | op->flags = (pc->onlink ? OPT_PX_ONLINK : 0) | | |
292 | (pc->autonomous ? OPT_PX_AUTONOMOUS : 0); | |
36da2857 OZ |
293 | op->valid_lifetime = (ra->active || !pc->valid_lifetime_sensitive) ? |
294 | htonl(pc->valid_lifetime) : 0; | |
295 | op->preferred_lifetime = (ra->active || !pc->preferred_lifetime_sensitive) ? | |
296 | htonl(pc->preferred_lifetime) : 0; | |
93e868c7 OZ |
297 | op->reserved = 0; |
298 | op->prefix = addr->prefix; | |
299 | ipa_hton(op->prefix); | |
300 | buf += sizeof(*op); | |
301 | } | |
fc06fb62 | 302 | |
36da2857 | 303 | if (! ic->rdnss_local) |
fc06fb62 OZ |
304 | if (radv_prepare_rdnss(ifa, &cf->rdnss_list, &buf, bufend) < 0) |
305 | goto done; | |
306 | ||
36da2857 | 307 | if (radv_prepare_rdnss(ifa, &ic->rdnss_list, &buf, bufend) < 0) |
fc06fb62 OZ |
308 | goto done; |
309 | ||
36da2857 | 310 | if (! ic->dnssl_local) |
fc06fb62 OZ |
311 | if (radv_prepare_dnssl(ifa, &cf->dnssl_list, &buf, bufend) < 0) |
312 | goto done; | |
313 | ||
36da2857 | 314 | if (radv_prepare_dnssl(ifa, &ic->dnssl_list, &buf, bufend) < 0) |
fc06fb62 OZ |
315 | goto done; |
316 | ||
317 | done: | |
93e868c7 OZ |
318 | ifa->plen = buf - bufstart; |
319 | } | |
320 | ||
321 | ||
322 | void | |
323 | radv_send_ra(struct radv_iface *ifa, int shutdown) | |
324 | { | |
325 | struct proto_radv *ra = ifa->ra; | |
326 | ||
327 | /* We store prepared RA in tbuf */ | |
328 | if (!ifa->plen) | |
329 | radv_prepare_ra(ifa); | |
330 | ||
331 | if (shutdown) | |
332 | { | |
333 | /* Modify router lifetime to 0, it is not restored because | |
334 | we suppose that the iface will be removed */ | |
335 | struct radv_ra_packet *pkt = (void *) ifa->sk->tbuf; | |
336 | pkt->router_lifetime = 0; | |
337 | } | |
338 | ||
339 | RADV_TRACE(D_PACKETS, "Sending RA via %s", ifa->iface->name); | |
340 | sk_send_to(ifa->sk, ifa->plen, AllNodes, 0); | |
341 | } | |
342 | ||
343 | ||
344 | static int | |
345 | radv_rx_hook(sock *sk, int size) | |
346 | { | |
347 | struct radv_iface *ifa = sk->data; | |
348 | struct proto_radv *ra = ifa->ra; | |
349 | ||
350 | /* We want just packets from sk->iface */ | |
351 | if (sk->lifindex != sk->iface->index) | |
352 | return 1; | |
353 | ||
354 | if (ipa_equal(sk->faddr, ifa->addr->ip)) | |
355 | return 1; | |
356 | ||
357 | if (size < 8) | |
358 | return 1; | |
359 | ||
360 | byte *buf = sk->rbuf; | |
361 | ||
362 | if (buf[1] != 0) | |
363 | return 1; | |
364 | ||
365 | /* Validation is a bit sloppy - Hop Limit is not checked and | |
366 | length of options is ignored for RS and left to later for RA */ | |
367 | ||
368 | switch (buf[0]) | |
369 | { | |
370 | case ICMPV6_RS: | |
371 | RADV_TRACE(D_PACKETS, "Received RS from %I via %s", | |
372 | sk->faddr, ifa->iface->name); | |
373 | radv_iface_notify(ifa, RA_EV_RS); | |
374 | return 1; | |
375 | ||
376 | case ICMPV6_RA: | |
377 | RADV_TRACE(D_PACKETS, "Received RA from %I via %s", | |
378 | sk->faddr, ifa->iface->name); | |
379 | /* FIXME - there should be some checking of received RAs, but we just ignore them */ | |
380 | return 1; | |
381 | ||
382 | default: | |
383 | return 1; | |
384 | } | |
385 | } | |
386 | ||
387 | static void | |
388 | radv_tx_hook(sock *sk) | |
389 | { | |
390 | struct radv_iface *ifa = sk->data; | |
391 | log(L_WARN "%s: TX hook called", ifa->ra->p.name); | |
392 | } | |
393 | ||
394 | static void | |
395 | radv_err_hook(sock *sk, int err) | |
396 | { | |
397 | struct radv_iface *ifa = sk->data; | |
e1afee27 | 398 | log(L_ERR "%s: Socket error on %s: %M", ifa->ra->p.name, ifa->iface->name, err); |
93e868c7 OZ |
399 | } |
400 | ||
401 | int | |
402 | radv_sk_open(struct radv_iface *ifa) | |
403 | { | |
404 | sock *sk = sk_new(ifa->ra->p.pool); | |
405 | sk->type = SK_IP; | |
406 | sk->dport = ICMPV6_PROTO; | |
407 | sk->saddr = IPA_NONE; | |
408 | ||
409 | sk->ttl = 255; /* Mandatory for Neighbor Discovery packets */ | |
410 | sk->rx_hook = radv_rx_hook; | |
411 | sk->tx_hook = radv_tx_hook; | |
412 | sk->err_hook = radv_err_hook; | |
413 | sk->iface = ifa->iface; | |
414 | sk->rbsize = 1024; // bufsize(ifa); | |
415 | sk->tbsize = 1024; // bufsize(ifa); | |
416 | sk->data = ifa; | |
417 | sk->flags = SKF_LADDR_RX; | |
418 | ||
419 | if (sk_open(sk) != 0) | |
420 | goto err; | |
421 | ||
422 | sk->saddr = ifa->addr->ip; | |
423 | ||
93e868c7 OZ |
424 | /* We want listen just to ICMPv6 messages of type RS and RA */ |
425 | if (sk_set_icmp_filter(sk, ICMPV6_RS, ICMPV6_RA) < 0) | |
426 | goto err; | |
427 | ||
428 | if (sk_setup_multicast(sk) < 0) | |
429 | goto err; | |
430 | ||
431 | if (sk_join_group(sk, AllRouters) < 0) | |
432 | goto err; | |
433 | ||
434 | ifa->sk = sk; | |
435 | return 1; | |
436 | ||
437 | err: | |
438 | rfree(sk); | |
439 | return 0; | |
440 | } | |
441 |