]>
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 | 253 | pkt->current_hop_limit = ic->current_hop_limit; |
36da2857 OZ |
254 | pkt->router_lifetime = (ra->active || !ic->default_lifetime_sensitive) ? |
255 | htons(ic->default_lifetime) : 0; | |
75148289 OZ |
256 | pkt->flags = (ic->managed ? OPT_RA_MANAGED : 0) | |
257 | (ic->other_config ? OPT_RA_OTHER_CFG : 0) | | |
258 | (pkt->router_lifetime ? ic->default_preference : 0); | |
36da2857 OZ |
259 | pkt->reachable_time = htonl(ic->reachable_time); |
260 | pkt->retrans_timer = htonl(ic->retrans_timer); | |
93e868c7 OZ |
261 | buf += sizeof(*pkt); |
262 | ||
36da2857 | 263 | if (ic->link_mtu) |
93e868c7 OZ |
264 | { |
265 | struct radv_opt_mtu *om = (void *) buf; | |
266 | om->type = OPT_MTU; | |
267 | om->length = 1; | |
268 | om->reserved = 0; | |
36da2857 | 269 | om->mtu = htonl(ic->link_mtu); |
93e868c7 OZ |
270 | buf += sizeof (*om); |
271 | } | |
272 | ||
273 | struct ifa *addr; | |
274 | WALK_LIST(addr, ifa->iface->addrs) | |
275 | { | |
276 | struct radv_prefix_config *pc; | |
277 | pc = radv_prefix_match(ifa, addr); | |
278 | ||
279 | if (!pc || pc->skip) | |
280 | continue; | |
281 | ||
282 | if (buf + sizeof(struct radv_opt_prefix) > bufend) | |
283 | { | |
284 | log(L_WARN "%s: Too many prefixes on interface %s", ra->p.name, ifa->iface->name); | |
fc06fb62 | 285 | goto done; |
93e868c7 OZ |
286 | } |
287 | ||
288 | struct radv_opt_prefix *op = (void *) buf; | |
289 | op->type = OPT_PREFIX; | |
290 | op->length = 4; | |
291 | op->pxlen = addr->pxlen; | |
292 | op->flags = (pc->onlink ? OPT_PX_ONLINK : 0) | | |
293 | (pc->autonomous ? OPT_PX_AUTONOMOUS : 0); | |
36da2857 OZ |
294 | op->valid_lifetime = (ra->active || !pc->valid_lifetime_sensitive) ? |
295 | htonl(pc->valid_lifetime) : 0; | |
296 | op->preferred_lifetime = (ra->active || !pc->preferred_lifetime_sensitive) ? | |
297 | htonl(pc->preferred_lifetime) : 0; | |
93e868c7 OZ |
298 | op->reserved = 0; |
299 | op->prefix = addr->prefix; | |
300 | ipa_hton(op->prefix); | |
301 | buf += sizeof(*op); | |
302 | } | |
fc06fb62 | 303 | |
36da2857 | 304 | if (! ic->rdnss_local) |
fc06fb62 OZ |
305 | if (radv_prepare_rdnss(ifa, &cf->rdnss_list, &buf, bufend) < 0) |
306 | goto done; | |
307 | ||
36da2857 | 308 | if (radv_prepare_rdnss(ifa, &ic->rdnss_list, &buf, bufend) < 0) |
fc06fb62 OZ |
309 | goto done; |
310 | ||
36da2857 | 311 | if (! ic->dnssl_local) |
fc06fb62 OZ |
312 | if (radv_prepare_dnssl(ifa, &cf->dnssl_list, &buf, bufend) < 0) |
313 | goto done; | |
314 | ||
36da2857 | 315 | if (radv_prepare_dnssl(ifa, &ic->dnssl_list, &buf, bufend) < 0) |
fc06fb62 OZ |
316 | goto done; |
317 | ||
318 | done: | |
93e868c7 OZ |
319 | ifa->plen = buf - bufstart; |
320 | } | |
321 | ||
322 | ||
323 | void | |
324 | radv_send_ra(struct radv_iface *ifa, int shutdown) | |
325 | { | |
326 | struct proto_radv *ra = ifa->ra; | |
327 | ||
328 | /* We store prepared RA in tbuf */ | |
329 | if (!ifa->plen) | |
330 | radv_prepare_ra(ifa); | |
331 | ||
332 | if (shutdown) | |
333 | { | |
75148289 OZ |
334 | /* |
335 | * Modify router lifetime to 0, it is not restored because we suppose that | |
336 | * the iface will be removed. The preference value also has to be zeroed. | |
337 | * (RFC 4191 2.2: If router lifetime is 0, the preference value must be 0.) | |
338 | */ | |
339 | ||
93e868c7 OZ |
340 | struct radv_ra_packet *pkt = (void *) ifa->sk->tbuf; |
341 | pkt->router_lifetime = 0; | |
75148289 | 342 | pkt->flags &= ~RA_PREF_MASK; |
93e868c7 OZ |
343 | } |
344 | ||
345 | RADV_TRACE(D_PACKETS, "Sending RA via %s", ifa->iface->name); | |
88a183c6 | 346 | sk_send_to(ifa->sk, ifa->plen, IP6_ALL_NODES, 0); |
93e868c7 OZ |
347 | } |
348 | ||
349 | ||
350 | static int | |
351 | radv_rx_hook(sock *sk, int size) | |
352 | { | |
353 | struct radv_iface *ifa = sk->data; | |
354 | struct proto_radv *ra = ifa->ra; | |
355 | ||
356 | /* We want just packets from sk->iface */ | |
357 | if (sk->lifindex != sk->iface->index) | |
358 | return 1; | |
359 | ||
360 | if (ipa_equal(sk->faddr, ifa->addr->ip)) | |
361 | return 1; | |
362 | ||
363 | if (size < 8) | |
364 | return 1; | |
365 | ||
366 | byte *buf = sk->rbuf; | |
367 | ||
368 | if (buf[1] != 0) | |
369 | return 1; | |
370 | ||
371 | /* Validation is a bit sloppy - Hop Limit is not checked and | |
372 | length of options is ignored for RS and left to later for RA */ | |
373 | ||
374 | switch (buf[0]) | |
375 | { | |
376 | case ICMPV6_RS: | |
377 | RADV_TRACE(D_PACKETS, "Received RS from %I via %s", | |
378 | sk->faddr, ifa->iface->name); | |
379 | radv_iface_notify(ifa, RA_EV_RS); | |
380 | return 1; | |
381 | ||
382 | case ICMPV6_RA: | |
383 | RADV_TRACE(D_PACKETS, "Received RA from %I via %s", | |
384 | sk->faddr, ifa->iface->name); | |
385 | /* FIXME - there should be some checking of received RAs, but we just ignore them */ | |
386 | return 1; | |
387 | ||
388 | default: | |
389 | return 1; | |
390 | } | |
391 | } | |
392 | ||
393 | static void | |
394 | radv_tx_hook(sock *sk) | |
395 | { | |
396 | struct radv_iface *ifa = sk->data; | |
397 | log(L_WARN "%s: TX hook called", ifa->ra->p.name); | |
398 | } | |
399 | ||
400 | static void | |
401 | radv_err_hook(sock *sk, int err) | |
402 | { | |
403 | struct radv_iface *ifa = sk->data; | |
e1afee27 | 404 | log(L_ERR "%s: Socket error on %s: %M", ifa->ra->p.name, ifa->iface->name, err); |
93e868c7 OZ |
405 | } |
406 | ||
407 | int | |
408 | radv_sk_open(struct radv_iface *ifa) | |
409 | { | |
410 | sock *sk = sk_new(ifa->ra->p.pool); | |
411 | sk->type = SK_IP; | |
412 | sk->dport = ICMPV6_PROTO; | |
48e5f32d | 413 | sk->saddr = ifa->addr->ip; |
93e868c7 OZ |
414 | |
415 | sk->ttl = 255; /* Mandatory for Neighbor Discovery packets */ | |
416 | sk->rx_hook = radv_rx_hook; | |
417 | sk->tx_hook = radv_tx_hook; | |
418 | sk->err_hook = radv_err_hook; | |
419 | sk->iface = ifa->iface; | |
420 | sk->rbsize = 1024; // bufsize(ifa); | |
421 | sk->tbsize = 1024; // bufsize(ifa); | |
422 | sk->data = ifa; | |
423 | sk->flags = SKF_LADDR_RX; | |
424 | ||
05476c4d | 425 | if (sk_open(sk) < 0) |
93e868c7 OZ |
426 | goto err; |
427 | ||
93e868c7 | 428 | /* We want listen just to ICMPv6 messages of type RS and RA */ |
05476c4d | 429 | if (sk_set_icmp6_filter(sk, ICMPV6_RS, ICMPV6_RA) < 0) |
93e868c7 OZ |
430 | goto err; |
431 | ||
432 | if (sk_setup_multicast(sk) < 0) | |
433 | goto err; | |
434 | ||
88a183c6 | 435 | if (sk_join_group(sk, IP6_ALL_ROUTERS) < 0) |
93e868c7 OZ |
436 | goto err; |
437 | ||
438 | ifa->sk = sk; | |
439 | return 1; | |
440 | ||
441 | err: | |
05476c4d | 442 | sk_log_error(sk, ifa->ra->p.name); |
93e868c7 OZ |
443 | rfree(sk); |
444 | return 0; | |
445 | } | |
446 |