From 4e3e6679e8f73b83d38e4b20d8b025e12991d1cb Mon Sep 17 00:00:00 2001 From: Patrik Flykt Date: Fri, 10 Jul 2015 11:25:21 +0300 Subject: [PATCH 01/11] sd-dhcp6-client: Fix unreferencing DHCPv6 lease on client reset When the DHCPv6 client is started by the library user or stopped for any reason, unref the DHCPv6 lease when resetting the DHCPv6 client data structure. This makes the DHCPv6 client always start from a clean state and not keep unnecessary an lease structure around when stopped. If this is not done, a previously existing lease information can be interpreted to be from another server when restarting DHCPv6. --- src/libsystemd-network/sd-dhcp6-client.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/libsystemd-network/sd-dhcp6-client.c b/src/libsystemd-network/sd-dhcp6-client.c index e2f58628514..85bb1f3a8f5 100644 --- a/src/libsystemd-network/sd-dhcp6-client.c +++ b/src/libsystemd-network/sd-dhcp6-client.c @@ -272,6 +272,11 @@ static void client_notify(sd_dhcp6_client *client, int event) { static int client_reset(sd_dhcp6_client *client) { assert_return(client, -EINVAL); + if (client->lease) { + dhcp6_lease_clear_timers(&client->lease->ia); + client->lease = sd_dhcp6_lease_unref(client->lease); + } + client->receive_message = sd_event_source_unref(client->receive_message); From 9d89d1ae71cb298218e35a69d6b70e2c94de5271 Mon Sep 17 00:00:00 2001 From: Patrik Flykt Date: Fri, 10 Jul 2015 11:31:50 +0300 Subject: [PATCH 02/11] sd-dhcp6-client: Save a DHCPv6 lease also with Information Reply As the lease structure contains interesting information, save it also for the Information Reply. --- src/libsystemd-network/sd-dhcp6-client.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/libsystemd-network/sd-dhcp6-client.c b/src/libsystemd-network/sd-dhcp6-client.c index 85bb1f3a8f5..a88a6f4b4c2 100644 --- a/src/libsystemd-network/sd-dhcp6-client.c +++ b/src/libsystemd-network/sd-dhcp6-client.c @@ -807,10 +807,8 @@ static int client_receive_reply(sd_dhcp6_client *client, DHCP6Message *reply, client->lease = sd_dhcp6_lease_unref(client->lease); } - if (client->state != DHCP6_STATE_INFORMATION_REQUEST) { - client->lease = lease; - lease = NULL; - } + client->lease = lease; + lease = NULL; return DHCP6_STATE_BOUND; } From b553817ccfc950df53b9d9870799919824945de5 Mon Sep 17 00:00:00 2001 From: Patrik Flykt Date: Thu, 2 Apr 2015 10:35:30 +0300 Subject: [PATCH 03/11] dhcp6-option: Add helper function for fetching IPv6 addresses Add a helper function that extracts a block of IPv6 addresses from the provided option data. --- src/libsystemd-network/dhcp6-internal.h | 5 ++++- src/libsystemd-network/dhcp6-option.c | 20 +++++++++++++++++++- 2 files changed, 23 insertions(+), 2 deletions(-) diff --git a/src/libsystemd-network/dhcp6-internal.h b/src/libsystemd-network/dhcp6-internal.h index 4f54ad89a67..87a3588db78 100644 --- a/src/libsystemd-network/dhcp6-internal.h +++ b/src/libsystemd-network/dhcp6-internal.h @@ -5,7 +5,7 @@ /*** This file is part of systemd. - Copyright (C) 2014 Intel Corporation. All rights reserved. + Copyright (C) 2014-2015 Intel Corporation. All rights reserved. systemd is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by @@ -68,6 +68,9 @@ int dhcp6_option_parse(uint8_t **buf, size_t *buflen, uint16_t *optcode, size_t *optlen, uint8_t **optvalue); int dhcp6_option_parse_ia(uint8_t **buf, size_t *buflen, uint16_t iatype, DHCP6IA *ia); +int dhcp6_option_parse_ip6addrs(uint8_t *optval, uint16_t optlen, + struct in6_addr **addrs, size_t count, + size_t *allocated); int dhcp6_network_bind_udp_socket(int index, struct in6_addr *address); int dhcp6_network_send_udp_socket(int s, struct in6_addr *address, diff --git a/src/libsystemd-network/dhcp6-option.c b/src/libsystemd-network/dhcp6-option.c index ea863f45e4e..693170bf0c7 100644 --- a/src/libsystemd-network/dhcp6-option.c +++ b/src/libsystemd-network/dhcp6-option.c @@ -3,7 +3,7 @@ /*** This file is part of systemd. - Copyright (C) 2014 Intel Corporation. All rights reserved. + Copyright (C) 2014-2015 Intel Corporation. All rights reserved. systemd is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by @@ -317,3 +317,21 @@ error: return r; } + +int dhcp6_option_parse_ip6addrs(uint8_t *optval, uint16_t optlen, + struct in6_addr **addrs, size_t count, + size_t *allocated) { + + if (optlen == 0 || optlen % sizeof(struct in6_addr) != 0) + return -EINVAL; + + if (!GREEDY_REALLOC(*addrs, *allocated, + count * sizeof(struct in6_addr) + optlen)) + return -ENOMEM; + + memcpy(*addrs + count, optval, optlen); + + count += optlen / sizeof(struct in6_addr); + + return count; +} From f96ccab7e0e6cb96851010682abbc1ac0909264d Mon Sep 17 00:00:00 2001 From: Patrik Flykt Date: Mon, 4 May 2015 13:23:46 +0300 Subject: [PATCH 04/11] dhcp6-option: Add helper function for uncompressed domain names Add a helper function containing a modified version of dns_packet_read_name() that does not use DnsPacket to extract a string array of domain names from the provided option data. The domain names are stored uncompressed as defined in Section 8. of RFC 3315. --- src/libsystemd-network/dhcp6-internal.h | 2 + src/libsystemd-network/dhcp6-option.c | 82 +++++++++++++++++++++++++ 2 files changed, 84 insertions(+) diff --git a/src/libsystemd-network/dhcp6-internal.h b/src/libsystemd-network/dhcp6-internal.h index 87a3588db78..83e8192f584 100644 --- a/src/libsystemd-network/dhcp6-internal.h +++ b/src/libsystemd-network/dhcp6-internal.h @@ -71,6 +71,8 @@ int dhcp6_option_parse_ia(uint8_t **buf, size_t *buflen, uint16_t iatype, int dhcp6_option_parse_ip6addrs(uint8_t *optval, uint16_t optlen, struct in6_addr **addrs, size_t count, size_t *allocated); +int dhcp6_option_parse_domainname(const uint8_t *optval, uint16_t optlen, + char ***str_arr); int dhcp6_network_bind_udp_socket(int index, struct in6_addr *address); int dhcp6_network_send_udp_socket(int s, struct in6_addr *address, diff --git a/src/libsystemd-network/dhcp6-option.c b/src/libsystemd-network/dhcp6-option.c index 693170bf0c7..6da7ea7e278 100644 --- a/src/libsystemd-network/dhcp6-option.c +++ b/src/libsystemd-network/dhcp6-option.c @@ -26,9 +26,11 @@ #include "sparse-endian.h" #include "unaligned.h" #include "util.h" +#include "strv.h" #include "dhcp6-internal.h" #include "dhcp6-protocol.h" +#include "dns-domain.h" #define DHCP6_OPTION_IA_NA_LEN 12 #define DHCP6_OPTION_IA_TA_LEN 4 @@ -335,3 +337,83 @@ int dhcp6_option_parse_ip6addrs(uint8_t *optval, uint16_t optlen, return count; } + +int dhcp6_option_parse_domainname(const uint8_t *optval, uint16_t optlen, + char ***str_arr) +{ + size_t pos = 0, idx = 0; + _cleanup_free_ char **names = NULL; + int r; + + assert_return(optlen > 1, -ENODATA); + assert_return(optval[optlen] == '\0', -EINVAL); + + while (pos < optlen) { + _cleanup_free_ char *ret = NULL; + size_t n = 0, allocated = 0; + bool first = true; + + for (;;) { + uint8_t c; + + c = optval[pos++]; + + if (c == 0) + /* End of name */ + break; + else if (c <= 63) { + _cleanup_free_ char *t = NULL; + const char *label; + + /* Literal label */ + label = (const char *)&optval[pos]; + pos += c; + if (pos > optlen) + return -EMSGSIZE; + + r = dns_label_escape(label, c, &t); + if (r < 0) + goto fail; + + if (!GREEDY_REALLOC0(ret, allocated, n + !first + strlen(t) + 1)) { + r = -ENOMEM; + goto fail; + } + + if (!first) + ret[n++] = '.'; + else + first = false; + + memcpy(ret + n, t, r); + n += r; + continue; + } else { + r = -EBADMSG; + goto fail; + } + } + + if (!GREEDY_REALLOC(ret, allocated, n + 1)) { + r = -ENOMEM; + goto fail; + } + + ret[n] = 0; + + r = strv_extend(&names, ret); + if (r < 0) + goto fail; + + ret = NULL; + idx++; + } + + *str_arr = names; + names = NULL; + + return idx; + +fail: + return r; +} From 7bd8e95d44977833d0de3fc4e893eb3bc84351d6 Mon Sep 17 00:00:00 2001 From: Patrik Flykt Date: Thu, 2 Apr 2015 10:50:16 +0300 Subject: [PATCH 05/11] sd-dhcp6: Add support for DHCPv6 DNS Recursive Name Server option Support DHCPv6 DNS server option as specified in RFC 3646. This option contains a list of IPv6 DNS server addresses. --- src/libsystemd-network/dhcp6-lease-internal.h | 7 +++- src/libsystemd-network/sd-dhcp6-client.c | 9 ++++- src/libsystemd-network/sd-dhcp6-lease.c | 37 +++++++++++++++++++ src/systemd/sd-dhcp6-lease.h | 4 +- 4 files changed, 54 insertions(+), 3 deletions(-) diff --git a/src/libsystemd-network/dhcp6-lease-internal.h b/src/libsystemd-network/dhcp6-lease-internal.h index 109e0f4f21e..0dad0a5132c 100644 --- a/src/libsystemd-network/dhcp6-lease-internal.h +++ b/src/libsystemd-network/dhcp6-lease-internal.h @@ -6,7 +6,7 @@ This file is part of systemd. Copyright (C) 2014 Tom Gundersen - Copyright (C) 2014 Intel Corporation. All rights reserved. + Copyright (C) 2014-2015 Intel Corporation. All rights reserved. systemd is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by @@ -40,6 +40,10 @@ struct sd_dhcp6_lease { DHCP6IA ia; DHCP6Address *addr_iter; + + struct in6_addr *dns; + size_t dns_count; + size_t dns_allocated; }; int dhcp6_lease_clear_timers(DHCP6IA *ia); @@ -56,6 +60,7 @@ int dhcp6_lease_get_rapid_commit(sd_dhcp6_lease *lease, bool *rapid_commit); int dhcp6_lease_get_iaid(sd_dhcp6_lease *lease, be32_t *iaid); +int dhcp6_lease_set_dns(sd_dhcp6_lease *lease, uint8_t *optval, size_t optlen); int dhcp6_lease_new(sd_dhcp6_lease **ret); DEFINE_TRIVIAL_CLEANUP_FUNC(sd_dhcp6_lease*, sd_dhcp6_lease_unref); diff --git a/src/libsystemd-network/sd-dhcp6-client.c b/src/libsystemd-network/sd-dhcp6-client.c index a88a6f4b4c2..627ddb63c60 100644 --- a/src/libsystemd-network/sd-dhcp6-client.c +++ b/src/libsystemd-network/sd-dhcp6-client.c @@ -3,7 +3,7 @@ /*** This file is part of systemd. - Copyright (C) 2014 Intel Corporation. All rights reserved. + Copyright (C) 2014-2015 Intel Corporation. All rights reserved. systemd is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by @@ -753,6 +753,13 @@ static int client_parse_message(sd_dhcp6_client *client, return r; break; + + case DHCP6_OPTION_DNS_SERVERS: + r = dhcp6_lease_set_dns(lease, optval, optlen); + if (r < 0) + return r; + + break; } } diff --git a/src/libsystemd-network/sd-dhcp6-lease.c b/src/libsystemd-network/sd-dhcp6-lease.c index 2442269a3f5..55cefdeec9e 100644 --- a/src/libsystemd-network/sd-dhcp6-lease.c +++ b/src/libsystemd-network/sd-dhcp6-lease.c @@ -173,6 +173,42 @@ void sd_dhcp6_lease_reset_address_iter(sd_dhcp6_lease *lease) { lease->addr_iter = lease->ia.addresses; } +int dhcp6_lease_set_dns(sd_dhcp6_lease *lease, uint8_t *optval, size_t optlen) { + int r; + + assert_return(lease, -EINVAL); + assert_return(optval, -EINVAL); + + if (!optlen) + return 0; + + r = dhcp6_option_parse_ip6addrs(optval, optlen, &lease->dns, + lease->dns_count, + &lease->dns_allocated); + if (r < 0) { + log_dhcp6_client(client, "Invalid DNS server option: %s", + strerror(-r)); + + return r; + } + + lease->dns_count = r; + + return 0; +} + +int sd_dhcp6_lease_get_dns(sd_dhcp6_lease *lease, struct in6_addr **addrs) { + assert_return(lease, -EINVAL); + assert_return(addrs, -EINVAL); + + if (lease->dns_count) { + *addrs = lease->dns; + return lease->dns_count; + } + + return -ENOENT; +} + sd_dhcp6_lease *sd_dhcp6_lease_ref(sd_dhcp6_lease *lease) { if (lease) assert_se(REFCNT_INC(lease->n_ref) >= 2); @@ -185,6 +221,7 @@ sd_dhcp6_lease *sd_dhcp6_lease_unref(sd_dhcp6_lease *lease) { free(lease->serverid); dhcp6_lease_free_ia(&lease->ia); + free(lease->dns); free(lease); } diff --git a/src/systemd/sd-dhcp6-lease.h b/src/systemd/sd-dhcp6-lease.h index 716d7678f10..c98026bd0f7 100644 --- a/src/systemd/sd-dhcp6-lease.h +++ b/src/systemd/sd-dhcp6-lease.h @@ -7,7 +7,7 @@ This file is part of systemd. Copyright (C) 2014 Tom Gundersen - Copyright (C) 2014 Intel Corporation. All rights reserved. + Copyright (C) 2014-2015 Intel Corporation. All rights reserved. systemd is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by @@ -33,6 +33,8 @@ int sd_dhcp6_lease_get_address(sd_dhcp6_lease *lease, uint32_t *lifetime_preferred, uint32_t *lifetime_valid); +int sd_dhcp6_lease_get_dns(sd_dhcp6_lease *lease, struct in6_addr **addrs); + sd_dhcp6_lease *sd_dhcp6_lease_ref(sd_dhcp6_lease *lease); sd_dhcp6_lease *sd_dhcp6_lease_unref(sd_dhcp6_lease *lease); From 5da1b97f3c3d15521f2dcfbc18eccd6580122ebc Mon Sep 17 00:00:00 2001 From: Patrik Flykt Date: Thu, 2 Apr 2015 15:34:12 +0300 Subject: [PATCH 06/11] sd-dhcp6: Add support for DHCPv6 DNS Domain Search List option Support DHCPv6 DNS search list option as specified in RFC 3646. This option contains a list of DNS search domains encoded without compression as specified in Section 8. of RFC 3315. --- src/libsystemd-network/dhcp6-lease-internal.h | 4 ++ src/libsystemd-network/sd-dhcp6-client.c | 8 ++++ src/libsystemd-network/sd-dhcp6-lease.c | 42 ++++++++++++++++++- src/systemd/sd-dhcp6-lease.h | 1 + 4 files changed, 54 insertions(+), 1 deletion(-) diff --git a/src/libsystemd-network/dhcp6-lease-internal.h b/src/libsystemd-network/dhcp6-lease-internal.h index 0dad0a5132c..37e70013200 100644 --- a/src/libsystemd-network/dhcp6-lease-internal.h +++ b/src/libsystemd-network/dhcp6-lease-internal.h @@ -44,6 +44,8 @@ struct sd_dhcp6_lease { struct in6_addr *dns; size_t dns_count; size_t dns_allocated; + char **domains; + size_t domains_count; }; int dhcp6_lease_clear_timers(DHCP6IA *ia); @@ -61,6 +63,8 @@ int dhcp6_lease_get_rapid_commit(sd_dhcp6_lease *lease, bool *rapid_commit); int dhcp6_lease_get_iaid(sd_dhcp6_lease *lease, be32_t *iaid); int dhcp6_lease_set_dns(sd_dhcp6_lease *lease, uint8_t *optval, size_t optlen); +int dhcp6_lease_set_domains(sd_dhcp6_lease *lease, uint8_t *optval, + size_t optlen); int dhcp6_lease_new(sd_dhcp6_lease **ret); DEFINE_TRIVIAL_CLEANUP_FUNC(sd_dhcp6_lease*, sd_dhcp6_lease_unref); diff --git a/src/libsystemd-network/sd-dhcp6-client.c b/src/libsystemd-network/sd-dhcp6-client.c index 627ddb63c60..6eaae1bd9f1 100644 --- a/src/libsystemd-network/sd-dhcp6-client.c +++ b/src/libsystemd-network/sd-dhcp6-client.c @@ -760,6 +760,14 @@ static int client_parse_message(sd_dhcp6_client *client, return r; break; + + case DHCP6_OPTION_DOMAIN_LIST: + r = dhcp6_lease_set_domains(lease, optval, optlen); + if (r < 0) + return r; + + break; + } } diff --git a/src/libsystemd-network/sd-dhcp6-lease.c b/src/libsystemd-network/sd-dhcp6-lease.c index 55cefdeec9e..ca638009f11 100644 --- a/src/libsystemd-network/sd-dhcp6-lease.c +++ b/src/libsystemd-network/sd-dhcp6-lease.c @@ -1,8 +1,10 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + /*** This file is part of systemd. Copyright (C) 2014 Tom Gundersen - Copyright (C) 2014 Intel Corporation. All rights reserved. + Copyright (C) 2014-2015 Intel Corporation. All rights reserved. systemd is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by @@ -20,6 +22,7 @@ #include +#include "strv.h" #include "util.h" #include "dhcp6-lease-internal.h" @@ -209,6 +212,40 @@ int sd_dhcp6_lease_get_dns(sd_dhcp6_lease *lease, struct in6_addr **addrs) { return -ENOENT; } +int dhcp6_lease_set_domains(sd_dhcp6_lease *lease, uint8_t *optval, + size_t optlen) { + int r; + char **domains; + + assert_return(lease, -EINVAL); + assert_return(optval, -EINVAL); + + if (!optlen) + return 0; + + r = dhcp6_option_parse_domainname(optval, optlen, &domains); + if (r < 0) + return 0; + + free(lease->domains); + lease->domains = domains; + lease->domains_count = r; + + return r; +} + +int sd_dhcp6_lease_get_domains(sd_dhcp6_lease *lease, char ***domains) { + assert_return(lease, -EINVAL); + assert_return(domains, -EINVAL); + + if (lease->domains_count) { + *domains = lease->domains; + return lease->domains_count; + } + + return -ENOENT; +} + sd_dhcp6_lease *sd_dhcp6_lease_ref(sd_dhcp6_lease *lease) { if (lease) assert_se(REFCNT_INC(lease->n_ref) >= 2); @@ -222,6 +259,9 @@ sd_dhcp6_lease *sd_dhcp6_lease_unref(sd_dhcp6_lease *lease) { dhcp6_lease_free_ia(&lease->ia); free(lease->dns); + + lease->domains = strv_free(lease->domains); + free(lease); } diff --git a/src/systemd/sd-dhcp6-lease.h b/src/systemd/sd-dhcp6-lease.h index c98026bd0f7..8fdb48edebb 100644 --- a/src/systemd/sd-dhcp6-lease.h +++ b/src/systemd/sd-dhcp6-lease.h @@ -34,6 +34,7 @@ int sd_dhcp6_lease_get_address(sd_dhcp6_lease *lease, uint32_t *lifetime_valid); int sd_dhcp6_lease_get_dns(sd_dhcp6_lease *lease, struct in6_addr **addrs); +int sd_dhcp6_lease_get_domains(sd_dhcp6_lease *lease, char ***domains); sd_dhcp6_lease *sd_dhcp6_lease_ref(sd_dhcp6_lease *lease); sd_dhcp6_lease *sd_dhcp6_lease_unref(sd_dhcp6_lease *lease); From 6599680e2d33597f0f11a99e1c3c957b42418568 Mon Sep 17 00:00:00 2001 From: Patrik Flykt Date: Fri, 10 Apr 2015 15:59:00 +0300 Subject: [PATCH 07/11] sd-dhcp6: Add support for DHCPv6 NTP Server Option Support NTP server and multicast addresses and NTP server domain names as specified in RFC 5908. --- src/libsystemd-network/dhcp6-lease-internal.h | 7 ++ src/libsystemd-network/dhcp6-protocol.h | 6 ++ src/libsystemd-network/sd-dhcp6-client.c | 7 ++ src/libsystemd-network/sd-dhcp6-lease.c | 81 +++++++++++++++++++ src/systemd/sd-dhcp6-lease.h | 3 + 5 files changed, 104 insertions(+) diff --git a/src/libsystemd-network/dhcp6-lease-internal.h b/src/libsystemd-network/dhcp6-lease-internal.h index 37e70013200..ac7f84366d0 100644 --- a/src/libsystemd-network/dhcp6-lease-internal.h +++ b/src/libsystemd-network/dhcp6-lease-internal.h @@ -46,6 +46,11 @@ struct sd_dhcp6_lease { size_t dns_allocated; char **domains; size_t domains_count; + struct in6_addr *ntp; + size_t ntp_count; + size_t ntp_allocated; + char **ntp_fqdn; + size_t ntp_fqdn_count; }; int dhcp6_lease_clear_timers(DHCP6IA *ia); @@ -65,6 +70,8 @@ int dhcp6_lease_get_iaid(sd_dhcp6_lease *lease, be32_t *iaid); int dhcp6_lease_set_dns(sd_dhcp6_lease *lease, uint8_t *optval, size_t optlen); int dhcp6_lease_set_domains(sd_dhcp6_lease *lease, uint8_t *optval, size_t optlen); +int dhcp6_lease_set_ntp(sd_dhcp6_lease *lease, uint8_t *optval, size_t optlen); + int dhcp6_lease_new(sd_dhcp6_lease **ret); DEFINE_TRIVIAL_CLEANUP_FUNC(sd_dhcp6_lease*, sd_dhcp6_lease_unref); diff --git a/src/libsystemd-network/dhcp6-protocol.h b/src/libsystemd-network/dhcp6-protocol.h index 3e0f339237f..9330f239b59 100644 --- a/src/libsystemd-network/dhcp6-protocol.h +++ b/src/libsystemd-network/dhcp6-protocol.h @@ -133,6 +133,12 @@ enum { /* option codes 144-65535 are unassigned */ }; +enum { + DHCP6_NTP_SUBOPTION_SRV_ADDR = 1, + DHCP6_NTP_SUBOPTION_MC_ADDR = 2, + DHCP6_NTP_SUBOPTION_SRV_FQDN = 3, +}; + enum { DHCP6_STATUS_SUCCESS = 0, DHCP6_STATUS_UNSPEC_FAIL = 1, diff --git a/src/libsystemd-network/sd-dhcp6-client.c b/src/libsystemd-network/sd-dhcp6-client.c index 6eaae1bd9f1..986958d8adc 100644 --- a/src/libsystemd-network/sd-dhcp6-client.c +++ b/src/libsystemd-network/sd-dhcp6-client.c @@ -768,7 +768,14 @@ static int client_parse_message(sd_dhcp6_client *client, break; + case DHCP6_OPTION_NTP_SERVER: + r = dhcp6_lease_set_ntp(lease, optval, optlen); + if (r < 0) + return r; + + break; } + } if (r == -ENOMSG) diff --git a/src/libsystemd-network/sd-dhcp6-lease.c b/src/libsystemd-network/sd-dhcp6-lease.c index ca638009f11..8631aabc401 100644 --- a/src/libsystemd-network/sd-dhcp6-lease.c +++ b/src/libsystemd-network/sd-dhcp6-lease.c @@ -26,6 +26,7 @@ #include "util.h" #include "dhcp6-lease-internal.h" +#include "dhcp6-protocol.h" int dhcp6_lease_clear_timers(DHCP6IA *ia) { assert_return(ia, -EINVAL); @@ -246,6 +247,83 @@ int sd_dhcp6_lease_get_domains(sd_dhcp6_lease *lease, char ***domains) { return -ENOENT; } +int dhcp6_lease_set_ntp(sd_dhcp6_lease *lease, uint8_t *optval, size_t optlen) +{ + int r; + uint16_t subopt; + size_t sublen; + uint8_t *subval; + + assert_return(lease, -EINVAL); + assert_return(optval, -EINVAL); + + while ((r = dhcp6_option_parse(&optval, &optlen, &subopt, &sublen, + &subval)) >= 0) { + int s; + char **servers; + + switch(subopt) { + case DHCP6_NTP_SUBOPTION_SRV_ADDR: + case DHCP6_NTP_SUBOPTION_MC_ADDR: + if (sublen != 16) + return 0; + + s = dhcp6_option_parse_ip6addrs(subval, sublen, + &lease->ntp, + lease->ntp_count, + &lease->ntp_allocated); + if (s < 0) + return s; + + lease->ntp_count = s; + + break; + + case DHCP6_NTP_SUBOPTION_SRV_FQDN: + r = dhcp6_option_parse_domainname(subval, sublen, + &servers); + if (r < 0) + return 0; + + lease->ntp_fqdn = strv_free(lease->ntp_fqdn); + lease->ntp_fqdn = servers; + lease->ntp_fqdn_count = r; + + break; + } + } + + if (r != -ENOMSG) + return r; + + return 0; +} + +int sd_dhcp6_lease_get_ntp_addrs(sd_dhcp6_lease *lease, + struct in6_addr **addrs) { + assert_return(lease, -EINVAL); + assert_return(addrs, -EINVAL); + + if (lease->ntp_count) { + *addrs = lease->ntp; + return lease->ntp_count; + } + + return -ENOENT; +} + +int sd_dhcp6_lease_get_ntp_fqdn(sd_dhcp6_lease *lease, char ***ntp_fqdn) { + assert_return(lease, -EINVAL); + assert_return(ntp_fqdn, -EINVAL); + + if (lease->ntp_fqdn_count) { + *ntp_fqdn = lease->ntp_fqdn; + return lease->ntp_fqdn_count; + } + + return -ENOENT; +} + sd_dhcp6_lease *sd_dhcp6_lease_ref(sd_dhcp6_lease *lease) { if (lease) assert_se(REFCNT_INC(lease->n_ref) >= 2); @@ -262,6 +340,9 @@ sd_dhcp6_lease *sd_dhcp6_lease_unref(sd_dhcp6_lease *lease) { lease->domains = strv_free(lease->domains); + free(lease->ntp); + + lease->ntp_fqdn = strv_free(lease->ntp_fqdn); free(lease); } diff --git a/src/systemd/sd-dhcp6-lease.h b/src/systemd/sd-dhcp6-lease.h index 8fdb48edebb..dc3df3bbf7b 100644 --- a/src/systemd/sd-dhcp6-lease.h +++ b/src/systemd/sd-dhcp6-lease.h @@ -35,6 +35,9 @@ int sd_dhcp6_lease_get_address(sd_dhcp6_lease *lease, int sd_dhcp6_lease_get_dns(sd_dhcp6_lease *lease, struct in6_addr **addrs); int sd_dhcp6_lease_get_domains(sd_dhcp6_lease *lease, char ***domains); +int sd_dhcp6_lease_get_ntp_addrs(sd_dhcp6_lease *lease, + struct in6_addr **addrs); +int sd_dhcp6_lease_get_ntp_fqdn(sd_dhcp6_lease *lease, char ***ntp_fqdn); sd_dhcp6_lease *sd_dhcp6_lease_ref(sd_dhcp6_lease *lease); sd_dhcp6_lease *sd_dhcp6_lease_unref(sd_dhcp6_lease *lease); From 41e4615d4f4f5c61afa84ba857f23c0ac496687b Mon Sep 17 00:00:00 2001 From: Patrik Flykt Date: Fri, 10 Apr 2015 16:17:22 +0300 Subject: [PATCH 08/11] sd-dhcp6: Support deprecated SNTP Configuration Option Although the SNTP option specified in RFC 4075 has been deprecated, some servers are still sending NTP information with this option. Use the SNTP information provided only if the NTP option is not present. Update the test case as SNTP information is also requested. --- src/libsystemd-network/dhcp6-lease-internal.h | 2 ++ src/libsystemd-network/dhcp6-protocol.h | 2 +- src/libsystemd-network/sd-dhcp6-client.c | 8 +++++ src/libsystemd-network/sd-dhcp6-lease.c | 36 +++++++++++++++++++ src/libsystemd-network/test-dhcp6-client.c | 2 +- 5 files changed, 48 insertions(+), 2 deletions(-) diff --git a/src/libsystemd-network/dhcp6-lease-internal.h b/src/libsystemd-network/dhcp6-lease-internal.h index ac7f84366d0..037f580eb65 100644 --- a/src/libsystemd-network/dhcp6-lease-internal.h +++ b/src/libsystemd-network/dhcp6-lease-internal.h @@ -71,6 +71,8 @@ int dhcp6_lease_set_dns(sd_dhcp6_lease *lease, uint8_t *optval, size_t optlen); int dhcp6_lease_set_domains(sd_dhcp6_lease *lease, uint8_t *optval, size_t optlen); int dhcp6_lease_set_ntp(sd_dhcp6_lease *lease, uint8_t *optval, size_t optlen); +int dhcp6_lease_set_sntp(sd_dhcp6_lease *lease, uint8_t *optval, + size_t optlen) ; int dhcp6_lease_new(sd_dhcp6_lease **ret); diff --git a/src/libsystemd-network/dhcp6-protocol.h b/src/libsystemd-network/dhcp6-protocol.h index 9330f239b59..b3a28f88b4f 100644 --- a/src/libsystemd-network/dhcp6-protocol.h +++ b/src/libsystemd-network/dhcp6-protocol.h @@ -123,7 +123,7 @@ enum { DHCP6_OPTION_DNS_SERVERS = 23, /* RFC 3646 */ DHCP6_OPTION_DOMAIN_LIST = 24, /* RFC 3646 */ - DHCP6_OPTION_SNTP_SERVERS = 31, /* RFC 4075 */ + DHCP6_OPTION_SNTP_SERVERS = 31, /* RFC 4075, deprecated */ /* option code 35 is unassigned */ diff --git a/src/libsystemd-network/sd-dhcp6-client.c b/src/libsystemd-network/sd-dhcp6-client.c index 986958d8adc..bc17c6adc50 100644 --- a/src/libsystemd-network/sd-dhcp6-client.c +++ b/src/libsystemd-network/sd-dhcp6-client.c @@ -73,6 +73,7 @@ static const uint16_t default_req_opts[] = { DHCP6_OPTION_DNS_SERVERS, DHCP6_OPTION_DOMAIN_LIST, DHCP6_OPTION_NTP_SERVER, + DHCP6_OPTION_SNTP_SERVERS, }; const char * dhcp6_message_type_table[_DHCP6_MESSAGE_MAX] = { @@ -774,6 +775,13 @@ static int client_parse_message(sd_dhcp6_client *client, return r; break; + + case DHCP6_OPTION_SNTP_SERVERS: + r = dhcp6_lease_set_sntp(lease, optval, optlen); + if (r < 0) + return r; + + break; } } diff --git a/src/libsystemd-network/sd-dhcp6-lease.c b/src/libsystemd-network/sd-dhcp6-lease.c index 8631aabc401..f0494b3c911 100644 --- a/src/libsystemd-network/sd-dhcp6-lease.c +++ b/src/libsystemd-network/sd-dhcp6-lease.c @@ -257,6 +257,10 @@ int dhcp6_lease_set_ntp(sd_dhcp6_lease *lease, uint8_t *optval, size_t optlen) assert_return(lease, -EINVAL); assert_return(optval, -EINVAL); + free(lease->ntp); + lease->ntp_count = 0; + lease->ntp_allocated = 0; + while ((r = dhcp6_option_parse(&optval, &optlen, &subopt, &sublen, &subval)) >= 0) { int s; @@ -299,6 +303,38 @@ int dhcp6_lease_set_ntp(sd_dhcp6_lease *lease, uint8_t *optval, size_t optlen) return 0; } +int dhcp6_lease_set_sntp(sd_dhcp6_lease *lease, uint8_t *optval, size_t optlen) { + int r; + + assert_return(lease, -EINVAL); + assert_return(optval, -EINVAL); + + if (!optlen) + return 0; + + if (lease->ntp || lease->ntp_fqdn) { + log_dhcp6_client(client, "NTP information already provided"); + + return 0; + } + + log_dhcp6_client(client, "Using deprecated SNTP information"); + + r = dhcp6_option_parse_ip6addrs(optval, optlen, &lease->ntp, + lease->ntp_count, + &lease->ntp_allocated); + if (r < 0) { + log_dhcp6_client(client, "Invalid SNTP server option: %s", + strerror(-r)); + + return r; + } + + lease->ntp_count = r; + + return 0; +} + int sd_dhcp6_lease_get_ntp_addrs(sd_dhcp6_lease *lease, struct in6_addr **addrs) { assert_return(lease, -EINVAL); diff --git a/src/libsystemd-network/test-dhcp6-client.c b/src/libsystemd-network/test-dhcp6-client.c index 761854714bd..b61fd38adef 100644 --- a/src/libsystemd-network/test-dhcp6-client.c +++ b/src/libsystemd-network/test-dhcp6-client.c @@ -73,7 +73,7 @@ static int test_client_basic(sd_event *e) { assert_se(sd_dhcp6_client_set_request_option(client, DHCP6_OPTION_CLIENTID) == -EINVAL); assert_se(sd_dhcp6_client_set_request_option(client, DHCP6_OPTION_DNS_SERVERS) == -EEXIST); assert_se(sd_dhcp6_client_set_request_option(client, DHCP6_OPTION_NTP_SERVER) == -EEXIST); - assert_se(sd_dhcp6_client_set_request_option(client, DHCP6_OPTION_SNTP_SERVERS) == 0); + assert_se(sd_dhcp6_client_set_request_option(client, DHCP6_OPTION_SNTP_SERVERS) == -EEXIST); assert_se(sd_dhcp6_client_set_request_option(client, DHCP6_OPTION_DOMAIN_LIST) == -EEXIST); assert_se(sd_dhcp6_client_set_request_option(client, 10) == -EINVAL); From bc152ff879a79412e358daaaa3677a0fc226bcb0 Mon Sep 17 00:00:00 2001 From: Patrik Flykt Date: Fri, 10 Jul 2015 11:42:11 +0300 Subject: [PATCH 09/11] test-dhcp6-client: Add tests for DNS and NTP options Test option setting and getting in test_advertise_option(). Verify that the information provided in DHCPv6 Reply messages is also available in the Information and Solicit callbacks. --- src/libsystemd-network/test-dhcp6-client.c | 63 ++++++++++++++++++++++ 1 file changed, 63 insertions(+) diff --git a/src/libsystemd-network/test-dhcp6-client.c b/src/libsystemd-network/test-dhcp6-client.c index b61fd38adef..6e622624439 100644 --- a/src/libsystemd-network/test-dhcp6-client.c +++ b/src/libsystemd-network/test-dhcp6-client.c @@ -216,6 +216,8 @@ static int test_advertise_option(sd_event *e) { uint32_t lt_pref, lt_valid; int r; bool opt_clientid = false; + struct in6_addr *addrs; + char **domains; if (verbose) printf("* %s\n", __FUNCTION__); @@ -276,6 +278,24 @@ static int test_advertise_option(sd_event *e) { break; + case DHCP6_OPTION_DNS_SERVERS: + assert_se(optlen == 16); + assert_se(dhcp6_lease_set_dns(lease, optval, + optlen) >= 0); + break; + + case DHCP6_OPTION_DOMAIN_LIST: + assert_se(optlen == 11); + assert_se(dhcp6_lease_set_domains(lease, optval, + optlen) >= 0); + break; + + case DHCP6_OPTION_SNTP_SERVERS: + assert_se(optlen == 16); + assert_se(dhcp6_lease_set_sntp(lease, optval, + optlen) >= 0); + break; + default: break; } @@ -315,6 +335,19 @@ static int test_advertise_option(sd_event *e) { assert_se(dhcp6_lease_get_preference(lease, &preference) >= 0); assert_se(preference == 0); + r = sd_dhcp6_lease_get_dns(lease, &addrs); + assert_se(r == 1); + assert_se(!memcmp(addrs, &msg_advertise[124], r * 16)); + + r = sd_dhcp6_lease_get_domains(lease, &domains); + assert_se(r == 1); + assert_se(!strcmp("lab.intra", domains[0])); + assert_se(domains[1] == NULL); + + r = sd_dhcp6_lease_get_ntp_addrs(lease, &addrs); + assert_se(r == 1); + assert_se(!memcmp(addrs, &msg_advertise[159], r * 16)); + return 0; } @@ -339,10 +372,25 @@ int detect_virtualization(const char **id) { static void test_client_solicit_cb(sd_dhcp6_client *client, int event, void *userdata) { sd_event *e = userdata; + sd_dhcp6_lease *lease; + struct in6_addr *addrs; + char **domains; assert_se(e); assert_se(event == DHCP6_EVENT_IP_ACQUIRE); + assert_se(sd_dhcp6_client_get_lease(client, &lease) >= 0); + + assert_se(sd_dhcp6_lease_get_domains(lease, &domains) == 1); + assert_se(!strcmp("lab.intra", domains[0])); + assert_se(domains[1] == NULL); + + assert_se(sd_dhcp6_lease_get_dns(lease, &addrs) == 1); + assert_se(!memcmp(addrs, &msg_advertise[124], 16)); + + assert_se(sd_dhcp6_lease_get_ntp_addrs(lease, &addrs) == 1); + assert_se(!memcmp(addrs, &msg_advertise[159], 16)); + assert_se(sd_dhcp6_client_set_request_option(client, DHCP6_OPTION_DNS_SERVERS) == -EBUSY); if (verbose) @@ -524,10 +572,25 @@ static int test_client_verify_solicit(DHCP6Message *solicit, uint8_t *option, static void test_client_information_cb(sd_dhcp6_client *client, int event, void *userdata) { sd_event *e = userdata; + sd_dhcp6_lease *lease; + struct in6_addr *addrs; + char **domains; assert_se(e); assert_se(event == DHCP6_EVENT_INFORMATION_REQUEST); + assert_se(sd_dhcp6_client_get_lease(client, &lease) >= 0); + + assert_se(sd_dhcp6_lease_get_domains(lease, &domains) == 1); + assert_se(!strcmp("lab.intra", domains[0])); + assert_se(domains[1] == NULL); + + assert_se(sd_dhcp6_lease_get_dns(lease, &addrs) == 1); + assert_se(!memcmp(addrs, &msg_advertise[124], 16)); + + assert_se(sd_dhcp6_lease_get_ntp_addrs(lease, &addrs) == 1); + assert_se(!memcmp(addrs, &msg_advertise[159], 16)); + if (verbose) printf(" got DHCPv6 event %d\n", event); From b729fa14aa118d4c7cd6846ff66d8cf1e885d900 Mon Sep 17 00:00:00 2001 From: Patrik Flykt Date: Mon, 6 Jul 2015 12:50:47 +0300 Subject: [PATCH 10/11] network: Add function to serialize an IPv6 address --- src/libsystemd-network/network-internal.c | 15 +++++++++++++++ src/libsystemd-network/network-internal.h | 2 ++ 2 files changed, 17 insertions(+) diff --git a/src/libsystemd-network/network-internal.c b/src/libsystemd-network/network-internal.c index 3d78bf8b35f..d8357c687e9 100644 --- a/src/libsystemd-network/network-internal.c +++ b/src/libsystemd-network/network-internal.c @@ -32,6 +32,7 @@ #include "conf-parser.h" #include "condition.h" #include "network-internal.h" +#include "sd-icmp6-nd.h" const char *net_get_name(struct udev_device *device) { const char *name, *field; @@ -384,6 +385,20 @@ int deserialize_in_addrs(struct in_addr **ret, const char *string) { return size; } +void serialize_in6_addrs(FILE *f, const struct in6_addr *addresses, + size_t size) { + unsigned i; + + assert(f); + assert(addresses); + assert(size); + + for (i = 0; i < size; i++) + fprintf(f, SD_ICMP6_ADDRESS_FORMAT_STR"%s", + SD_ICMP6_ADDRESS_FORMAT_VAL(addresses[i]), + (i < (size - 1)) ? " ": ""); +} + int deserialize_in6_addrs(struct in6_addr **ret, const char *string) { _cleanup_free_ struct in6_addr *addresses = NULL; int size = 0; diff --git a/src/libsystemd-network/network-internal.h b/src/libsystemd-network/network-internal.h index 7aaecbb10d9..dca82646cef 100644 --- a/src/libsystemd-network/network-internal.h +++ b/src/libsystemd-network/network-internal.h @@ -67,6 +67,8 @@ const char *net_get_name(struct udev_device *device); void serialize_in_addrs(FILE *f, const struct in_addr *addresses, size_t size); int deserialize_in_addrs(struct in_addr **addresses, const char *string); +void serialize_in6_addrs(FILE *f, const struct in6_addr *addresses, + size_t size); int deserialize_in6_addrs(struct in6_addr **addresses, const char *string); /* don't include "dhcp-lease-internal.h" as it causes conflicts between netinet/ip.h and linux/ip.h */ From 07bdc70d1685ded36a910c16502a1ee57ebb540c Mon Sep 17 00:00:00 2001 From: Patrik Flykt Date: Mon, 6 Jul 2015 15:00:12 +0300 Subject: [PATCH 11/11] network: Save DNS and NTP data for a DHCPv6 link Append DNS and NTP data obtained via DHCPv6 when the Link is saved. --- src/network/networkd-link.c | 62 +++++++++++++++++++++++++++++++++++++ 1 file changed, 62 insertions(+) diff --git a/src/network/networkd-link.c b/src/network/networkd-link.c index 78e96c4e5ba..91b9cdf30d4 100644 --- a/src/network/networkd-link.c +++ b/src/network/networkd-link.c @@ -2240,6 +2240,14 @@ int link_save(Link *link) { if (link->network) { char **address, **domain; bool space; + sd_dhcp6_lease *dhcp6_lease = NULL; + + if (link->dhcp6_client) { + r = sd_dhcp6_client_get_lease(link->dhcp6_client, + &dhcp6_lease); + if (r < 0) + log_link_debug(link, "No DHCPv6 lease"); + } fprintf(f, "NETWORK_FILE=%s\n", link->network->filename); @@ -2261,6 +2269,19 @@ int link_save(Link *link) { if (space) fputc(' ', f); serialize_in_addrs(f, addresses, r); + space = true; + } + } + + if (link->network->dhcp_dns && dhcp6_lease) { + struct in6_addr *in6_addrs; + + r = sd_dhcp6_lease_get_dns(dhcp6_lease, &in6_addrs); + if (r > 0) { + if (space) + fputc(' ', f); + serialize_in6_addrs(f, in6_addrs, r); + space = true; } } @@ -2284,6 +2305,32 @@ int link_save(Link *link) { if (space) fputc(' ', f); serialize_in_addrs(f, addresses, r); + space = true; + } + } + + if (link->network->dhcp_ntp && dhcp6_lease) { + struct in6_addr *in6_addrs; + char **hosts; + char **hostname; + + r = sd_dhcp6_lease_get_ntp_addrs(dhcp6_lease, + &in6_addrs); + if (r > 0) { + if (space) + fputc(' ', f); + serialize_in6_addrs(f, in6_addrs, r); + space = true; + } + + r = sd_dhcp6_lease_get_ntp_fqdn(dhcp6_lease, &hosts); + if (r > 0) { + STRV_FOREACH(hostname, hosts) { + if (space) + fputc(' ', f); + fputs(*hostname, f); + space = true; + } } } @@ -2307,6 +2354,21 @@ int link_save(Link *link) { if (space) fputc(' ', f); fputs(domainname, f); + space = true; + } + } + + if (link->network->dhcp_domains && dhcp6_lease) { + char **domains; + + r = sd_dhcp6_lease_get_domains(dhcp6_lease, &domains); + if (r >= 0) { + STRV_FOREACH(domain, domains) { + if (space) + fputc(' ', f); + fputs(*domain, f); + space = true; + } } }