]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/libsystemd-network/ndisc-router.c
Merge pull request #18990 from yuwata/network-dhcpv6-use-domains
[thirdparty/systemd.git] / src / libsystemd-network / ndisc-router.c
1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
2 /***
3 Copyright © 2014 Intel Corporation. All rights reserved.
4 ***/
5
6 #include <netinet/icmp6.h>
7
8 #include "sd-ndisc.h"
9
10 #include "alloc-util.h"
11 #include "dns-domain.h"
12 #include "hostname-util.h"
13 #include "memory-util.h"
14 #include "missing_network.h"
15 #include "ndisc-internal.h"
16 #include "ndisc-router.h"
17 #include "strv.h"
18
19 DEFINE_PUBLIC_TRIVIAL_REF_UNREF_FUNC(sd_ndisc_router, sd_ndisc_router, mfree);
20
21 sd_ndisc_router *ndisc_router_new(size_t raw_size) {
22 sd_ndisc_router *rt;
23
24 rt = malloc0(ALIGN(sizeof(sd_ndisc_router)) + raw_size);
25 if (!rt)
26 return NULL;
27
28 rt->raw_size = raw_size;
29 rt->n_ref = 1;
30
31 return rt;
32 }
33
34 _public_ int sd_ndisc_router_from_raw(sd_ndisc_router **ret, const void *raw, size_t raw_size) {
35 _cleanup_(sd_ndisc_router_unrefp) sd_ndisc_router *rt = NULL;
36 int r;
37
38 assert_return(ret, -EINVAL);
39 assert_return(raw || raw_size <= 0, -EINVAL);
40
41 rt = ndisc_router_new(raw_size);
42 if (!rt)
43 return -ENOMEM;
44
45 memcpy(NDISC_ROUTER_RAW(rt), raw, raw_size);
46 r = ndisc_router_parse(NULL, rt);
47 if (r < 0)
48 return r;
49
50 *ret = TAKE_PTR(rt);
51
52 return r;
53 }
54
55 _public_ int sd_ndisc_router_get_address(sd_ndisc_router *rt, struct in6_addr *ret_addr) {
56 assert_return(rt, -EINVAL);
57 assert_return(ret_addr, -EINVAL);
58
59 if (in6_addr_is_null(&rt->address))
60 return -ENODATA;
61
62 *ret_addr = rt->address;
63 return 0;
64 }
65
66 _public_ int sd_ndisc_router_get_timestamp(sd_ndisc_router *rt, clockid_t clock, uint64_t *ret) {
67 assert_return(rt, -EINVAL);
68 assert_return(TRIPLE_TIMESTAMP_HAS_CLOCK(clock), -EOPNOTSUPP);
69 assert_return(clock_supported(clock), -EOPNOTSUPP);
70 assert_return(ret, -EINVAL);
71
72 if (!triple_timestamp_is_set(&rt->timestamp))
73 return -ENODATA;
74
75 *ret = triple_timestamp_by_clock(&rt->timestamp, clock);
76 return 0;
77 }
78
79 _public_ int sd_ndisc_router_get_raw(sd_ndisc_router *rt, const void **ret, size_t *size) {
80 assert_return(rt, -EINVAL);
81 assert_return(ret, -EINVAL);
82 assert_return(size, -EINVAL);
83
84 *ret = NDISC_ROUTER_RAW(rt);
85 *size = rt->raw_size;
86
87 return 0;
88 }
89
90 int ndisc_router_parse(sd_ndisc *nd, sd_ndisc_router *rt) {
91 struct nd_router_advert *a;
92 const uint8_t *p;
93 bool has_mtu = false, has_flag_extension = false;
94 size_t left;
95
96 assert(rt);
97
98 if (rt->raw_size < sizeof(struct nd_router_advert))
99 return log_ndisc_errno(nd, SYNTHETIC_ERRNO(EBADMSG),
100 "Too small to be a router advertisement, ignoring.");
101
102 /* Router advertisement packets are neatly aligned to 64bit boundaries, hence we can access them directly */
103 a = NDISC_ROUTER_RAW(rt);
104
105 if (a->nd_ra_type != ND_ROUTER_ADVERT)
106 return log_ndisc_errno(nd, SYNTHETIC_ERRNO(EBADMSG),
107 "Received ND packet that is not a router advertisement, ignoring.");
108
109 if (a->nd_ra_code != 0)
110 return log_ndisc_errno(nd, SYNTHETIC_ERRNO(EBADMSG),
111 "Received ND packet with wrong RA code, ignoring.");
112
113 rt->hop_limit = a->nd_ra_curhoplimit;
114 rt->flags = a->nd_ra_flags_reserved; /* the first 8bit */
115 rt->lifetime = be16toh(a->nd_ra_router_lifetime);
116
117 rt->preference = (rt->flags >> 3) & 3;
118 if (!IN_SET(rt->preference, SD_NDISC_PREFERENCE_LOW, SD_NDISC_PREFERENCE_HIGH))
119 rt->preference = SD_NDISC_PREFERENCE_MEDIUM;
120
121 p = (const uint8_t*) NDISC_ROUTER_RAW(rt) + sizeof(struct nd_router_advert);
122 left = rt->raw_size - sizeof(struct nd_router_advert);
123
124 for (;;) {
125 uint8_t type;
126 size_t length;
127
128 if (left == 0)
129 break;
130
131 if (left < 2)
132 return log_ndisc_errno(nd, SYNTHETIC_ERRNO(EBADMSG),
133 "Option lacks header, ignoring datagram.");
134
135 type = p[0];
136 length = p[1] * 8;
137
138 if (length == 0)
139 return log_ndisc_errno(nd, SYNTHETIC_ERRNO(EBADMSG),
140 "Zero-length option, ignoring datagram.");
141 if (left < length)
142 return log_ndisc_errno(nd, SYNTHETIC_ERRNO(EBADMSG),
143 "Option truncated, ignoring datagram.");
144
145 switch (type) {
146
147 case SD_NDISC_OPTION_PREFIX_INFORMATION:
148
149 if (length != 4*8)
150 return log_ndisc_errno(nd, SYNTHETIC_ERRNO(EBADMSG),
151 "Prefix option of invalid size, ignoring datagram.");
152
153 if (p[2] > 128)
154 return log_ndisc_errno(nd, SYNTHETIC_ERRNO(EBADMSG),
155 "Bad prefix length, ignoring datagram.");
156
157 break;
158
159 case SD_NDISC_OPTION_MTU: {
160 uint32_t m;
161
162 if (has_mtu) {
163 log_ndisc(nd, "MTU option specified twice, ignoring.");
164 break;
165 }
166
167 if (length != 8)
168 return log_ndisc_errno(nd, SYNTHETIC_ERRNO(EBADMSG),
169 "MTU option of invalid size, ignoring datagram.");
170
171 m = be32toh(*(uint32_t*) (p + 4));
172 if (m >= IPV6_MIN_MTU) /* ignore invalidly small MTUs */
173 rt->mtu = m;
174
175 has_mtu = true;
176 break;
177 }
178
179 case SD_NDISC_OPTION_ROUTE_INFORMATION:
180 if (length < 1*8 || length > 3*8)
181 return log_ndisc_errno(nd, SYNTHETIC_ERRNO(EBADMSG),
182 "Route information option of invalid size, ignoring datagram.");
183
184 if (p[2] > 128)
185 return log_ndisc_errno(nd, SYNTHETIC_ERRNO(EBADMSG),
186 "Bad route prefix length, ignoring datagram.");
187
188 break;
189
190 case SD_NDISC_OPTION_RDNSS:
191 if (length < 3*8 || (length % (2*8)) != 1*8)
192 return log_ndisc_errno(nd, SYNTHETIC_ERRNO(EBADMSG), "RDNSS option has invalid size.");
193
194 break;
195
196 case SD_NDISC_OPTION_FLAGS_EXTENSION:
197
198 if (has_flag_extension) {
199 log_ndisc(nd, "Flags extension option specified twice, ignoring.");
200 break;
201 }
202
203 if (length < 1*8)
204 return log_ndisc_errno(nd, SYNTHETIC_ERRNO(EBADMSG),
205 "Flags extension option has invalid size.");
206
207 /* Add in the additional flags bits */
208 rt->flags |=
209 ((uint64_t) p[2] << 8) |
210 ((uint64_t) p[3] << 16) |
211 ((uint64_t) p[4] << 24) |
212 ((uint64_t) p[5] << 32) |
213 ((uint64_t) p[6] << 40) |
214 ((uint64_t) p[7] << 48);
215
216 has_flag_extension = true;
217 break;
218
219 case SD_NDISC_OPTION_DNSSL:
220 if (length < 2*8)
221 return log_ndisc_errno(nd, SYNTHETIC_ERRNO(EBADMSG),
222 "DNSSL option has invalid size.");
223
224 break;
225 }
226
227 p += length, left -= length;
228 }
229
230 rt->rindex = sizeof(struct nd_router_advert);
231 return 0;
232 }
233
234 _public_ int sd_ndisc_router_get_hop_limit(sd_ndisc_router *rt, uint8_t *ret) {
235 assert_return(rt, -EINVAL);
236 assert_return(ret, -EINVAL);
237
238 *ret = rt->hop_limit;
239 return 0;
240 }
241
242 _public_ int sd_ndisc_router_get_flags(sd_ndisc_router *rt, uint64_t *ret_flags) {
243 assert_return(rt, -EINVAL);
244 assert_return(ret_flags, -EINVAL);
245
246 *ret_flags = rt->flags;
247 return 0;
248 }
249
250 _public_ int sd_ndisc_router_get_lifetime(sd_ndisc_router *rt, uint16_t *ret_lifetime) {
251 assert_return(rt, -EINVAL);
252 assert_return(ret_lifetime, -EINVAL);
253
254 *ret_lifetime = rt->lifetime;
255 return 0;
256 }
257
258 _public_ int sd_ndisc_router_get_preference(sd_ndisc_router *rt, unsigned *ret) {
259 assert_return(rt, -EINVAL);
260 assert_return(ret, -EINVAL);
261
262 *ret = rt->preference;
263 return 0;
264 }
265
266 _public_ int sd_ndisc_router_get_mtu(sd_ndisc_router *rt, uint32_t *ret) {
267 assert_return(rt, -EINVAL);
268 assert_return(ret, -EINVAL);
269
270 if (rt->mtu <= 0)
271 return -ENODATA;
272
273 *ret = rt->mtu;
274 return 0;
275 }
276
277 _public_ int sd_ndisc_router_option_rewind(sd_ndisc_router *rt) {
278 assert_return(rt, -EINVAL);
279
280 assert(rt->raw_size >= sizeof(struct nd_router_advert));
281 rt->rindex = sizeof(struct nd_router_advert);
282
283 return rt->rindex < rt->raw_size;
284 }
285
286 _public_ int sd_ndisc_router_option_next(sd_ndisc_router *rt) {
287 size_t length;
288
289 assert_return(rt, -EINVAL);
290
291 if (rt->rindex == rt->raw_size) /* EOF */
292 return -ESPIPE;
293
294 if (rt->rindex + 2 > rt->raw_size) /* Truncated message */
295 return -EBADMSG;
296
297 length = NDISC_ROUTER_OPTION_LENGTH(rt);
298 if (rt->rindex + length > rt->raw_size)
299 return -EBADMSG;
300
301 rt->rindex += length;
302 return rt->rindex < rt->raw_size;
303 }
304
305 _public_ int sd_ndisc_router_option_get_type(sd_ndisc_router *rt, uint8_t *ret) {
306 assert_return(rt, -EINVAL);
307 assert_return(ret, -EINVAL);
308
309 if (rt->rindex == rt->raw_size) /* EOF */
310 return -ESPIPE;
311
312 if (rt->rindex + 2 > rt->raw_size) /* Truncated message */
313 return -EBADMSG;
314
315 *ret = NDISC_ROUTER_OPTION_TYPE(rt);
316 return 0;
317 }
318
319 _public_ int sd_ndisc_router_option_is_type(sd_ndisc_router *rt, uint8_t type) {
320 uint8_t k;
321 int r;
322
323 assert_return(rt, -EINVAL);
324
325 r = sd_ndisc_router_option_get_type(rt, &k);
326 if (r < 0)
327 return r;
328
329 return type == k;
330 }
331
332 _public_ int sd_ndisc_router_option_get_raw(sd_ndisc_router *rt, const void **ret, size_t *size) {
333 size_t length;
334
335 assert_return(rt, -EINVAL);
336 assert_return(ret, -EINVAL);
337 assert_return(size, -EINVAL);
338
339 /* Note that this returns the full option, including the option header */
340
341 if (rt->rindex + 2 > rt->raw_size)
342 return -EBADMSG;
343
344 length = NDISC_ROUTER_OPTION_LENGTH(rt);
345 if (rt->rindex + length > rt->raw_size)
346 return -EBADMSG;
347
348 *ret = (uint8_t*) NDISC_ROUTER_RAW(rt) + rt->rindex;
349 *size = length;
350
351 return 0;
352 }
353
354 static int get_prefix_info(sd_ndisc_router *rt, struct nd_opt_prefix_info **ret) {
355 struct nd_opt_prefix_info *ri;
356 size_t length;
357 int r;
358
359 assert(rt);
360 assert(ret);
361
362 r = sd_ndisc_router_option_is_type(rt, SD_NDISC_OPTION_PREFIX_INFORMATION);
363 if (r < 0)
364 return r;
365 if (r == 0)
366 return -EMEDIUMTYPE;
367
368 length = NDISC_ROUTER_OPTION_LENGTH(rt);
369 if (length != sizeof(struct nd_opt_prefix_info))
370 return -EBADMSG;
371
372 ri = (struct nd_opt_prefix_info*) ((uint8_t*) NDISC_ROUTER_RAW(rt) + rt->rindex);
373 if (ri->nd_opt_pi_prefix_len > 128)
374 return -EBADMSG;
375
376 *ret = ri;
377 return 0;
378 }
379
380 _public_ int sd_ndisc_router_prefix_get_valid_lifetime(sd_ndisc_router *rt, uint32_t *ret) {
381 struct nd_opt_prefix_info *ri;
382 int r;
383
384 assert_return(rt, -EINVAL);
385 assert_return(ret, -EINVAL);
386
387 r = get_prefix_info(rt, &ri);
388 if (r < 0)
389 return r;
390
391 *ret = be32toh(ri->nd_opt_pi_valid_time);
392 return 0;
393 }
394
395 _public_ int sd_ndisc_router_prefix_get_preferred_lifetime(sd_ndisc_router *rt, uint32_t *ret) {
396 struct nd_opt_prefix_info *pi;
397 int r;
398
399 assert_return(rt, -EINVAL);
400 assert_return(ret, -EINVAL);
401
402 r = get_prefix_info(rt, &pi);
403 if (r < 0)
404 return r;
405
406 *ret = be32toh(pi->nd_opt_pi_preferred_time);
407 return 0;
408 }
409
410 _public_ int sd_ndisc_router_prefix_get_flags(sd_ndisc_router *rt, uint8_t *ret) {
411 struct nd_opt_prefix_info *pi;
412 uint8_t flags;
413 int r;
414
415 assert_return(rt, -EINVAL);
416 assert_return(ret, -EINVAL);
417
418 r = get_prefix_info(rt, &pi);
419 if (r < 0)
420 return r;
421
422 flags = pi->nd_opt_pi_flags_reserved;
423
424 if ((flags & ND_OPT_PI_FLAG_AUTO) && (pi->nd_opt_pi_prefix_len != 64)) {
425 log_ndisc(NULL, "Invalid prefix length, ignoring prefix for stateless autoconfiguration.");
426 flags &= ~ND_OPT_PI_FLAG_AUTO;
427 }
428
429 *ret = flags;
430 return 0;
431 }
432
433 _public_ int sd_ndisc_router_prefix_get_address(sd_ndisc_router *rt, struct in6_addr *ret_addr) {
434 struct nd_opt_prefix_info *pi;
435 int r;
436
437 assert_return(rt, -EINVAL);
438 assert_return(ret_addr, -EINVAL);
439
440 r = get_prefix_info(rt, &pi);
441 if (r < 0)
442 return r;
443
444 *ret_addr = pi->nd_opt_pi_prefix;
445 return 0;
446 }
447
448 _public_ int sd_ndisc_router_prefix_get_prefixlen(sd_ndisc_router *rt, unsigned *ret) {
449 struct nd_opt_prefix_info *pi;
450 int r;
451
452 assert_return(rt, -EINVAL);
453 assert_return(ret, -EINVAL);
454
455 r = get_prefix_info(rt, &pi);
456 if (r < 0)
457 return r;
458
459 if (pi->nd_opt_pi_prefix_len > 128)
460 return -EBADMSG;
461
462 *ret = pi->nd_opt_pi_prefix_len;
463 return 0;
464 }
465
466 static int get_route_info(sd_ndisc_router *rt, uint8_t **ret) {
467 uint8_t *ri;
468 size_t length;
469 int r;
470
471 assert(rt);
472 assert(ret);
473
474 r = sd_ndisc_router_option_is_type(rt, SD_NDISC_OPTION_ROUTE_INFORMATION);
475 if (r < 0)
476 return r;
477 if (r == 0)
478 return -EMEDIUMTYPE;
479
480 length = NDISC_ROUTER_OPTION_LENGTH(rt);
481 if (length < 1*8 || length > 3*8)
482 return -EBADMSG;
483
484 ri = (uint8_t*) NDISC_ROUTER_RAW(rt) + rt->rindex;
485
486 if (ri[2] > 128)
487 return -EBADMSG;
488
489 *ret = ri;
490 return 0;
491 }
492
493 _public_ int sd_ndisc_router_route_get_lifetime(sd_ndisc_router *rt, uint32_t *ret) {
494 uint8_t *ri;
495 int r;
496
497 assert_return(rt, -EINVAL);
498 assert_return(ret, -EINVAL);
499
500 r = get_route_info(rt, &ri);
501 if (r < 0)
502 return r;
503
504 *ret = be32toh(*(uint32_t*) (ri + 4));
505 return 0;
506 }
507
508 _public_ int sd_ndisc_router_route_get_address(sd_ndisc_router *rt, struct in6_addr *ret_addr) {
509 uint8_t *ri;
510 int r;
511
512 assert_return(rt, -EINVAL);
513 assert_return(ret_addr, -EINVAL);
514
515 r = get_route_info(rt, &ri);
516 if (r < 0)
517 return r;
518
519 zero(*ret_addr);
520 memcpy(ret_addr, ri + 8, NDISC_ROUTER_OPTION_LENGTH(rt) - 8);
521
522 return 0;
523 }
524
525 _public_ int sd_ndisc_router_route_get_prefixlen(sd_ndisc_router *rt, unsigned *ret) {
526 uint8_t *ri;
527 int r;
528
529 assert_return(rt, -EINVAL);
530 assert_return(ret, -EINVAL);
531
532 r = get_route_info(rt, &ri);
533 if (r < 0)
534 return r;
535
536 *ret = ri[2];
537 return 0;
538 }
539
540 _public_ int sd_ndisc_router_route_get_preference(sd_ndisc_router *rt, unsigned *ret) {
541 uint8_t *ri;
542 int r;
543
544 assert_return(rt, -EINVAL);
545 assert_return(ret, -EINVAL);
546
547 r = get_route_info(rt, &ri);
548 if (r < 0)
549 return r;
550
551 *ret = (ri[3] >> 3) & 3;
552 if (!IN_SET(*ret, SD_NDISC_PREFERENCE_LOW, SD_NDISC_PREFERENCE_HIGH))
553 *ret = SD_NDISC_PREFERENCE_MEDIUM;
554
555 return 0;
556 }
557
558 static int get_rdnss_info(sd_ndisc_router *rt, uint8_t **ret) {
559 size_t length;
560 int r;
561
562 assert(rt);
563 assert(ret);
564
565 r = sd_ndisc_router_option_is_type(rt, SD_NDISC_OPTION_RDNSS);
566 if (r < 0)
567 return r;
568 if (r == 0)
569 return -EMEDIUMTYPE;
570
571 length = NDISC_ROUTER_OPTION_LENGTH(rt);
572 if (length < 3*8 || (length % (2*8)) != 1*8)
573 return -EBADMSG;
574
575 *ret = (uint8_t*) NDISC_ROUTER_RAW(rt) + rt->rindex;
576 return 0;
577 }
578
579 _public_ int sd_ndisc_router_rdnss_get_addresses(sd_ndisc_router *rt, const struct in6_addr **ret) {
580 uint8_t *ri;
581 int r;
582
583 assert_return(rt, -EINVAL);
584 assert_return(ret, -EINVAL);
585
586 r = get_rdnss_info(rt, &ri);
587 if (r < 0)
588 return r;
589
590 *ret = (const struct in6_addr*) (ri + 8);
591 return (NDISC_ROUTER_OPTION_LENGTH(rt) - 8) / 16;
592 }
593
594 _public_ int sd_ndisc_router_rdnss_get_lifetime(sd_ndisc_router *rt, uint32_t *ret) {
595 uint8_t *ri;
596 int r;
597
598 assert_return(rt, -EINVAL);
599 assert_return(ret, -EINVAL);
600
601 r = get_rdnss_info(rt, &ri);
602 if (r < 0)
603 return r;
604
605 *ret = be32toh(*(uint32_t*) (ri + 4));
606 return 0;
607 }
608
609 static int get_dnssl_info(sd_ndisc_router *rt, uint8_t **ret) {
610 size_t length;
611 int r;
612
613 assert(rt);
614 assert(ret);
615
616 r = sd_ndisc_router_option_is_type(rt, SD_NDISC_OPTION_DNSSL);
617 if (r < 0)
618 return r;
619 if (r == 0)
620 return -EMEDIUMTYPE;
621
622 length = NDISC_ROUTER_OPTION_LENGTH(rt);
623 if (length < 2*8)
624 return -EBADMSG;
625
626 *ret = (uint8_t*) NDISC_ROUTER_RAW(rt) + rt->rindex;
627 return 0;
628 }
629
630 _public_ int sd_ndisc_router_dnssl_get_domains(sd_ndisc_router *rt, char ***ret) {
631 _cleanup_strv_free_ char **l = NULL;
632 _cleanup_free_ char *e = NULL;
633 size_t allocated = 0, n = 0, left;
634 uint8_t *ri, *p;
635 bool first = true;
636 int r;
637 unsigned k = 0;
638
639 assert_return(rt, -EINVAL);
640 assert_return(ret, -EINVAL);
641
642 r = get_dnssl_info(rt, &ri);
643 if (r < 0)
644 return r;
645
646 p = ri + 8;
647 left = NDISC_ROUTER_OPTION_LENGTH(rt) - 8;
648
649 for (;;) {
650 if (left == 0) {
651
652 if (n > 0) /* Not properly NUL terminated */
653 return -EBADMSG;
654
655 break;
656 }
657
658 if (*p == 0) {
659 /* Found NUL termination */
660
661 if (n > 0) {
662 _cleanup_free_ char *normalized = NULL;
663
664 e[n] = 0;
665 r = dns_name_normalize(e, 0, &normalized);
666 if (r < 0)
667 return r;
668
669 /* Ignore the root domain name or "localhost" and friends */
670 if (!is_localhost(normalized) &&
671 !dns_name_is_root(normalized)) {
672
673 if (strv_push(&l, normalized) < 0)
674 return -ENOMEM;
675
676 normalized = NULL;
677 k++;
678 }
679 }
680
681 n = 0;
682 first = true;
683 p++, left--;
684 continue;
685 }
686
687 /* Check for compression (which is not allowed) */
688 if (*p > 63)
689 return -EBADMSG;
690
691 if (1U + *p + 1U > left)
692 return -EBADMSG;
693
694 if (!GREEDY_REALLOC(e, allocated, n + !first + DNS_LABEL_ESCAPED_MAX + 1U))
695 return -ENOMEM;
696
697 if (first)
698 first = false;
699 else
700 e[n++] = '.';
701
702 r = dns_label_escape((char*) p+1, *p, e + n, DNS_LABEL_ESCAPED_MAX);
703 if (r < 0)
704 return r;
705
706 n += r;
707
708 left -= 1 + *p;
709 p += 1 + *p;
710 }
711
712 if (strv_isempty(l)) {
713 *ret = NULL;
714 return 0;
715 }
716
717 *ret = TAKE_PTR(l);
718
719 return k;
720 }
721
722 _public_ int sd_ndisc_router_dnssl_get_lifetime(sd_ndisc_router *rt, uint32_t *ret_sec) {
723 uint8_t *ri;
724 int r;
725
726 assert_return(rt, -EINVAL);
727 assert_return(ret_sec, -EINVAL);
728
729 r = get_dnssl_info(rt, &ri);
730 if (r < 0)
731 return r;
732
733 *ret_sec = be32toh(*(uint32_t*) (ri + 4));
734 return 0;
735 }