]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/libsystemd-network/sd-ndisc.c
Merge pull request #1641 from poettering/btrfs-quota
[thirdparty/systemd.git] / src / libsystemd-network / sd-ndisc.c
1 /***
2 This file is part of systemd.
3
4 Copyright (C) 2014 Intel Corporation. All rights reserved.
5
6 systemd is free software; you can redistribute it and/or modify it
7 under the terms of the GNU Lesser General Public License as published by
8 the Free Software Foundation; either version 2.1 of the License, or
9 (at your option) any later version.
10
11 systemd is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 Lesser General Public License for more details.
15
16 You should have received a copy of the GNU Lesser General Public License
17 along with systemd; If not, see <http://www.gnu.org/licenses/>.
18 ***/
19
20 #include <netinet/icmp6.h>
21 #include <netinet/ip6.h>
22 #include <string.h>
23 #include <stdbool.h>
24 #include <netinet/in.h>
25 #include <sys/ioctl.h>
26
27 #include "async.h"
28 #include "list.h"
29 #include "socket-util.h"
30
31 #include "icmp6-util.h"
32 #include "sd-ndisc.h"
33
34 #define NDISC_ROUTER_SOLICITATION_INTERVAL 4 * USEC_PER_SEC
35 #define NDISC_MAX_ROUTER_SOLICITATIONS 3
36
37 enum NDiscState {
38 NDISC_STATE_IDLE,
39 NDISC_STATE_SOLICITATION_SENT,
40 NDISC_STATE_ADVERTISMENT_LISTEN,
41 _NDISC_STATE_MAX,
42 _NDISC_STATE_INVALID = -1,
43 };
44
45 #define IP6_MIN_MTU (unsigned)1280
46 #define ICMP6_RECV_SIZE (IP6_MIN_MTU - sizeof(struct ip6_hdr))
47 #define NDISC_OPT_LEN_UNITS 8
48
49 typedef struct NDiscPrefix NDiscPrefix;
50
51 struct NDiscPrefix {
52 unsigned n_ref;
53
54 sd_ndisc *nd;
55
56 LIST_FIELDS(NDiscPrefix, prefixes);
57
58 uint8_t len;
59 usec_t valid_until;
60 struct in6_addr addr;
61 };
62
63 struct sd_ndisc {
64 unsigned n_ref;
65
66 enum NDiscState state;
67 sd_event *event;
68 int event_priority;
69 int index;
70 struct ether_addr mac_addr;
71 uint32_t mtu;
72 LIST_HEAD(NDiscPrefix, prefixes);
73 int fd;
74 sd_event_source *recv;
75 sd_event_source *timeout;
76 int nd_sent;
77 sd_ndisc_callback_t callback;
78 void *userdata;
79 };
80
81 #define log_ndisc(p, fmt, ...) log_internal(LOG_DEBUG, 0, __FILE__, __LINE__, __func__, "NDisc CLIENT: " fmt, ##__VA_ARGS__)
82
83 static NDiscPrefix *ndisc_prefix_unref(NDiscPrefix *prefix) {
84
85 if (!prefix)
86 return NULL;
87
88 assert(prefix->n_ref > 0);
89 prefix->n_ref--;
90
91 if (prefix->n_ref > 0)
92 return NULL;
93
94 if (prefix->nd)
95 LIST_REMOVE(prefixes, prefix->nd->prefixes, prefix);
96
97 free(prefix);
98
99 return NULL;
100 }
101
102 static int ndisc_prefix_new(sd_ndisc *nd, NDiscPrefix **ret) {
103 _cleanup_free_ NDiscPrefix *prefix = NULL;
104
105 assert(ret);
106
107 prefix = new0(NDiscPrefix, 1);
108 if (!prefix)
109 return -ENOMEM;
110
111 prefix->n_ref = 1;
112 LIST_INIT(prefixes, prefix);
113 prefix->nd = nd;
114
115 *ret = prefix;
116 prefix = NULL;
117
118 return 0;
119 }
120
121 static void ndisc_notify(sd_ndisc *nd, int event) {
122 if (nd->callback)
123 nd->callback(nd, event, nd->userdata);
124 }
125
126 int sd_ndisc_set_callback(sd_ndisc *nd, sd_ndisc_callback_t callback,
127 void *userdata) {
128 assert(nd);
129
130 nd->callback = callback;
131 nd->userdata = userdata;
132
133 return 0;
134 }
135
136 int sd_ndisc_set_index(sd_ndisc *nd, int interface_index) {
137 assert(nd);
138 assert(interface_index >= -1);
139
140 nd->index = interface_index;
141
142 return 0;
143 }
144
145 int sd_ndisc_set_mac(sd_ndisc *nd, const struct ether_addr *mac_addr) {
146 assert(nd);
147
148 if (mac_addr)
149 memcpy(&nd->mac_addr, mac_addr, sizeof(nd->mac_addr));
150 else
151 zero(nd->mac_addr);
152
153 return 0;
154
155 }
156
157 int sd_ndisc_attach_event(sd_ndisc *nd, sd_event *event, int priority) {
158 int r;
159
160 assert_return(nd, -EINVAL);
161 assert_return(!nd->event, -EBUSY);
162
163 if (event)
164 nd->event = sd_event_ref(event);
165 else {
166 r = sd_event_default(&nd->event);
167 if (r < 0)
168 return 0;
169 }
170
171 nd->event_priority = priority;
172
173 return 0;
174 }
175
176 int sd_ndisc_detach_event(sd_ndisc *nd) {
177 assert_return(nd, -EINVAL);
178
179 nd->event = sd_event_unref(nd->event);
180
181 return 0;
182 }
183
184 sd_event *sd_ndisc_get_event(sd_ndisc *nd) {
185 assert(nd);
186
187 return nd->event;
188 }
189
190 sd_ndisc *sd_ndisc_ref(sd_ndisc *nd) {
191
192 if (!nd)
193 return NULL;
194
195 assert(nd->n_ref > 0);
196 nd->n_ref++;
197
198 return nd;
199 }
200
201 static int ndisc_init(sd_ndisc *nd) {
202 assert(nd);
203
204 nd->recv = sd_event_source_unref(nd->recv);
205 nd->fd = asynchronous_close(nd->fd);
206 nd->timeout = sd_event_source_unref(nd->timeout);
207
208 return 0;
209 }
210
211 sd_ndisc *sd_ndisc_unref(sd_ndisc *nd) {
212 NDiscPrefix *prefix, *p;
213
214 if (!nd)
215 return NULL;
216
217 assert(nd->n_ref > 0);
218 nd->n_ref--;
219
220 if (nd->n_ref > 0)
221 return NULL;
222
223 ndisc_init(nd);
224 sd_ndisc_detach_event(nd);
225
226 LIST_FOREACH_SAFE(prefixes, prefix, p, nd->prefixes)
227 prefix = ndisc_prefix_unref(prefix);
228
229 free(nd);
230
231 return NULL;
232 }
233
234 DEFINE_TRIVIAL_CLEANUP_FUNC(sd_ndisc*, sd_ndisc_unref);
235 #define _cleanup_sd_ndisc_free_ _cleanup_(sd_ndisc_unrefp)
236
237 int sd_ndisc_new(sd_ndisc **ret) {
238 _cleanup_sd_ndisc_free_ sd_ndisc *nd = NULL;
239
240 assert(ret);
241
242 nd = new0(sd_ndisc, 1);
243 if (!nd)
244 return -ENOMEM;
245
246 nd->n_ref = 1;
247
248 nd->index = -1;
249 nd->fd = -1;
250
251 LIST_HEAD_INIT(nd->prefixes);
252
253 *ret = nd;
254 nd = NULL;
255
256 return 0;
257 }
258
259 int sd_ndisc_get_mtu(sd_ndisc *nd, uint32_t *mtu) {
260 assert_return(nd, -EINVAL);
261 assert_return(mtu, -EINVAL);
262
263 if (nd->mtu == 0)
264 return -ENOMSG;
265
266 *mtu = nd->mtu;
267
268 return 0;
269 }
270
271 static int prefix_match(const struct in6_addr *prefix, uint8_t prefixlen,
272 const struct in6_addr *addr,
273 uint8_t addr_prefixlen) {
274 uint8_t bytes, mask, len;
275
276 assert_return(prefix, -EINVAL);
277 assert_return(addr, -EINVAL);
278
279 len = MIN(prefixlen, addr_prefixlen);
280
281 bytes = len / 8;
282 mask = 0xff << (8 - len % 8);
283
284 if (memcmp(prefix, addr, bytes) != 0 ||
285 (prefix->s6_addr[bytes] & mask) != (addr->s6_addr[bytes] & mask))
286 return -EADDRNOTAVAIL;
287
288 return 0;
289 }
290
291 static int ndisc_prefix_match(sd_ndisc *nd, const struct in6_addr *addr,
292 uint8_t addr_len, NDiscPrefix **result) {
293 NDiscPrefix *prefix, *p;
294 usec_t time_now;
295 int r;
296
297 assert(nd);
298
299 r = sd_event_now(nd->event, clock_boottime_or_monotonic(), &time_now);
300 if (r < 0)
301 return r;
302
303 LIST_FOREACH_SAFE(prefixes, prefix, p, nd->prefixes) {
304 if (prefix->valid_until < time_now) {
305 prefix = ndisc_prefix_unref(prefix);
306
307 continue;
308 }
309
310 if (prefix_match(&prefix->addr, prefix->len, addr, addr_len) >= 0) {
311 *result = prefix;
312 return 0;
313 }
314 }
315
316 return -EADDRNOTAVAIL;
317 }
318
319 static int ndisc_prefix_update(sd_ndisc *nd, ssize_t len,
320 const struct nd_opt_prefix_info *prefix_opt) {
321 NDiscPrefix *prefix;
322 uint32_t lifetime;
323 usec_t time_now;
324 char time_string[FORMAT_TIMESPAN_MAX];
325 int r;
326
327 assert(nd);
328 assert(prefix_opt);
329
330 if (len < prefix_opt->nd_opt_pi_len)
331 return -ENOMSG;
332
333 if (!(prefix_opt->nd_opt_pi_flags_reserved & ND_OPT_PI_FLAG_ONLINK))
334 return 0;
335
336 lifetime = be32toh(prefix_opt->nd_opt_pi_valid_time);
337
338 r = ndisc_prefix_match(nd, &prefix_opt->nd_opt_pi_prefix,
339 prefix_opt->nd_opt_pi_prefix_len, &prefix);
340
341 if (r < 0 && r != -EADDRNOTAVAIL)
342 return r;
343
344 /* if router advertisment prefix valid timeout is zero, the timeout
345 callback will be called immediately to clean up the prefix */
346
347 if (r == -EADDRNOTAVAIL) {
348 r = ndisc_prefix_new(nd, &prefix);
349 if (r < 0)
350 return r;
351
352 prefix->len = prefix_opt->nd_opt_pi_prefix_len;
353
354 memcpy(&prefix->addr, &prefix_opt->nd_opt_pi_prefix,
355 sizeof(prefix->addr));
356
357 log_ndisc(nd, "New prefix "SD_NDISC_ADDRESS_FORMAT_STR"/%d lifetime %d expires in %s",
358 SD_NDISC_ADDRESS_FORMAT_VAL(prefix->addr),
359 prefix->len, lifetime,
360 format_timespan(time_string, FORMAT_TIMESPAN_MAX, lifetime * USEC_PER_SEC, USEC_PER_SEC));
361
362 LIST_PREPEND(prefixes, nd->prefixes, prefix);
363
364 } else {
365 if (prefix->len != prefix_opt->nd_opt_pi_prefix_len) {
366 uint8_t prefixlen;
367
368 prefixlen = MIN(prefix->len, prefix_opt->nd_opt_pi_prefix_len);
369
370 log_ndisc(nd, "Prefix length mismatch %d/%d using %d",
371 prefix->len,
372 prefix_opt->nd_opt_pi_prefix_len,
373 prefixlen);
374
375 prefix->len = prefixlen;
376 }
377
378 log_ndisc(nd, "Update prefix "SD_NDISC_ADDRESS_FORMAT_STR"/%d lifetime %d expires in %s",
379 SD_NDISC_ADDRESS_FORMAT_VAL(prefix->addr),
380 prefix->len, lifetime,
381 format_timespan(time_string, FORMAT_TIMESPAN_MAX, lifetime * USEC_PER_SEC, USEC_PER_SEC));
382 }
383
384 r = sd_event_now(nd->event, clock_boottime_or_monotonic(), &time_now);
385 if (r < 0)
386 return r;
387
388 prefix->valid_until = time_now + lifetime * USEC_PER_SEC;
389
390 return r;
391 }
392
393 static int ndisc_ra_parse(sd_ndisc *nd, struct nd_router_advert *ra,
394 ssize_t len) {
395 void *opt;
396 struct nd_opt_hdr *opt_hdr;
397
398 assert_return(nd, -EINVAL);
399 assert_return(ra, -EINVAL);
400
401 len -= sizeof(*ra);
402 if (len < NDISC_OPT_LEN_UNITS) {
403 log_ndisc(nd, "Router Advertisement below minimum length");
404
405 return -ENOMSG;
406 }
407
408 opt = ra + 1;
409 opt_hdr = opt;
410
411 while (len != 0 && len >= opt_hdr->nd_opt_len * NDISC_OPT_LEN_UNITS) {
412 struct nd_opt_mtu *opt_mtu;
413 uint32_t mtu;
414 struct nd_opt_prefix_info *opt_prefix;
415
416 if (opt_hdr->nd_opt_len == 0)
417 return -ENOMSG;
418
419 switch (opt_hdr->nd_opt_type) {
420 case ND_OPT_MTU:
421 opt_mtu = opt;
422
423 mtu = be32toh(opt_mtu->nd_opt_mtu_mtu);
424
425 if (mtu != nd->mtu) {
426 nd->mtu = MAX(mtu, IP6_MIN_MTU);
427
428 log_ndisc(nd, "Router Advertisement link MTU %d using %d",
429 mtu, nd->mtu);
430 }
431
432 break;
433
434 case ND_OPT_PREFIX_INFORMATION:
435 opt_prefix = opt;
436
437 ndisc_prefix_update(nd, len, opt_prefix);
438
439 break;
440 }
441
442 len -= opt_hdr->nd_opt_len * NDISC_OPT_LEN_UNITS;
443 opt = (void *)((char *)opt +
444 opt_hdr->nd_opt_len * NDISC_OPT_LEN_UNITS);
445 opt_hdr = opt;
446 }
447
448 if (len > 0)
449 log_ndisc(nd, "Router Advertisement contains %zd bytes of trailing garbage", len);
450
451 return 0;
452 }
453
454 static int ndisc_router_advertisment_recv(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
455 sd_ndisc *nd = userdata;
456 int r, buflen = 0;
457 ssize_t len;
458 _cleanup_free_ struct nd_router_advert *ra = NULL;
459 int event = SD_NDISC_EVENT_ROUTER_ADVERTISMENT_NONE;
460
461 assert(s);
462 assert(nd);
463 assert(nd->event);
464
465 r = ioctl(fd, FIONREAD, &buflen);
466 if (r < 0 || buflen <= 0)
467 buflen = ICMP6_RECV_SIZE;
468
469 ra = malloc(buflen);
470 if (!ra)
471 return -ENOMEM;
472
473 len = read(fd, ra, buflen);
474 if (len < 0) {
475 log_ndisc(nd, "Could not receive message from UDP socket: %m");
476 return 0;
477 }
478
479 if (ra->nd_ra_type != ND_ROUTER_ADVERT)
480 return 0;
481
482 if (ra->nd_ra_code != 0)
483 return 0;
484
485 nd->timeout = sd_event_source_unref(nd->timeout);
486
487 nd->state = NDISC_STATE_ADVERTISMENT_LISTEN;
488
489 if (ra->nd_ra_flags_reserved & ND_RA_FLAG_OTHER )
490 event = SD_NDISC_EVENT_ROUTER_ADVERTISMENT_OTHER;
491
492 if (ra->nd_ra_flags_reserved & ND_RA_FLAG_MANAGED)
493 event = SD_NDISC_EVENT_ROUTER_ADVERTISMENT_MANAGED;
494
495 log_ndisc(nd, "Received Router Advertisement flags %s/%s",
496 ra->nd_ra_flags_reserved & ND_RA_FLAG_MANAGED? "MANAGED": "none",
497 ra->nd_ra_flags_reserved & ND_RA_FLAG_OTHER? "OTHER": "none");
498
499 if (event != SD_NDISC_EVENT_ROUTER_ADVERTISMENT_NONE) {
500 r = ndisc_ra_parse(nd, ra, len);
501 if (r < 0) {
502 log_ndisc(nd, "Could not parse Router Advertisement: %s",
503 strerror(-r));
504 return 0;
505 }
506 }
507
508 ndisc_notify(nd, event);
509
510 return 0;
511 }
512
513 static int ndisc_router_solicitation_timeout(sd_event_source *s, uint64_t usec, void *userdata) {
514 sd_ndisc *nd = userdata;
515 uint64_t time_now, next_timeout;
516 struct ether_addr unset = { };
517 struct ether_addr *addr = NULL;
518 int r;
519
520 assert(s);
521 assert(nd);
522 assert(nd->event);
523
524 nd->timeout = sd_event_source_unref(nd->timeout);
525
526 if (nd->nd_sent >= NDISC_MAX_ROUTER_SOLICITATIONS) {
527 ndisc_notify(nd, SD_NDISC_EVENT_ROUTER_ADVERTISMENT_TIMEOUT);
528 nd->state = NDISC_STATE_ADVERTISMENT_LISTEN;
529 } else {
530 if (memcmp(&nd->mac_addr, &unset, sizeof(struct ether_addr)))
531 addr = &nd->mac_addr;
532
533 r = icmp6_send_router_solicitation(nd->fd, addr);
534 if (r < 0)
535 log_ndisc(nd, "Error sending Router Solicitation");
536 else {
537 nd->state = NDISC_STATE_SOLICITATION_SENT;
538 log_ndisc(nd, "Sent Router Solicitation");
539 }
540
541 nd->nd_sent++;
542
543 assert_se(sd_event_now(nd->event, clock_boottime_or_monotonic(), &time_now) >= 0);
544
545 next_timeout = time_now + NDISC_ROUTER_SOLICITATION_INTERVAL;
546
547 r = sd_event_add_time(nd->event, &nd->timeout, clock_boottime_or_monotonic(),
548 next_timeout, 0,
549 ndisc_router_solicitation_timeout, nd);
550 if (r < 0) {
551 ndisc_notify(nd, r);
552 return 0;
553 }
554
555 r = sd_event_source_set_priority(nd->timeout, nd->event_priority);
556 if (r < 0)
557 return 0;
558
559 r = sd_event_source_set_description(nd->timeout, "ndisc-timeout");
560 if (r < 0)
561 return 0;
562 }
563
564 return 0;
565 }
566
567 int sd_ndisc_stop(sd_ndisc *nd) {
568 assert_return(nd, -EINVAL);
569 assert_return(nd->event, -EINVAL);
570
571 log_ndisc(client, "Stop NDisc");
572
573 ndisc_init(nd);
574
575 nd->state = NDISC_STATE_IDLE;
576
577 return 0;
578 }
579
580 int sd_ndisc_router_discovery_start(sd_ndisc *nd) {
581 int r;
582
583 assert(nd);
584 assert(nd->event);
585
586 if (nd->state != NDISC_STATE_IDLE)
587 return -EINVAL;
588
589 if (nd->index < 1)
590 return -EINVAL;
591
592 r = icmp6_bind_router_solicitation(nd->index);
593 if (r < 0)
594 return r;
595
596 nd->fd = r;
597
598 r = sd_event_add_io(nd->event, &nd->recv, nd->fd, EPOLLIN,
599 ndisc_router_advertisment_recv, nd);
600 if (r < 0)
601 goto error;
602
603 r = sd_event_source_set_priority(nd->recv, nd->event_priority);
604 if (r < 0)
605 goto error;
606
607 r = sd_event_source_set_description(nd->recv, "ndisc-receive-message");
608 if (r < 0)
609 goto error;
610
611 r = sd_event_add_time(nd->event, &nd->timeout, clock_boottime_or_monotonic(),
612 0, 0, ndisc_router_solicitation_timeout, nd);
613 if (r < 0)
614 goto error;
615
616 r = sd_event_source_set_priority(nd->timeout, nd->event_priority);
617 if (r < 0)
618 goto error;
619
620 r = sd_event_source_set_description(nd->timeout, "ndisc-timeout");
621 error:
622 if (r < 0)
623 ndisc_init(nd);
624 else
625 log_ndisc(client, "Start Router Solicitation");
626
627 return r;
628 }