1
0
mirror of https://github.com/systemd/systemd.git synced 2025-03-22 06:50:18 +03:00

Merge pull request #924 from pfl/systemd-dhcp6

sd-dhcpv6: support DNS and NTP information
This commit is contained in:
Tom Gundersen 2015-08-21 12:22:38 +02:00
commit 6b8b67e7ae
11 changed files with 516 additions and 12 deletions

View File

@ -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,11 @@ 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_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,

View File

@ -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,17 @@ struct sd_dhcp6_lease {
DHCP6IA ia;
DHCP6Address *addr_iter;
struct in6_addr *dns;
size_t dns_count;
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);
@ -56,6 +67,13 @@ 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_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);
DEFINE_TRIVIAL_CLEANUP_FUNC(sd_dhcp6_lease*, sd_dhcp6_lease_unref);

View File

@ -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
@ -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
@ -317,3 +319,101 @@ 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;
}
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;
}

View File

@ -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 */
@ -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,

View File

@ -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;

View File

@ -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 */

View File

@ -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
@ -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] = {
@ -272,6 +273,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);
@ -748,7 +754,36 @@ 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;
case DHCP6_OPTION_DOMAIN_LIST:
r = dhcp6_lease_set_domains(lease, optval, optlen);
if (r < 0)
return r;
break;
case DHCP6_OPTION_NTP_SERVER:
r = dhcp6_lease_set_ntp(lease, optval, optlen);
if (r < 0)
return r;
break;
case DHCP6_OPTION_SNTP_SERVERS:
r = dhcp6_lease_set_sntp(lease, optval, optlen);
if (r < 0)
return r;
break;
}
}
if (r == -ENOMSG)
@ -802,10 +837,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;
}

View File

@ -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,9 +22,11 @@
#include <errno.h>
#include "strv.h"
#include "util.h"
#include "dhcp6-lease-internal.h"
#include "dhcp6-protocol.h"
int dhcp6_lease_clear_timers(DHCP6IA *ia) {
assert_return(ia, -EINVAL);
@ -173,6 +177,189 @@ 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;
}
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;
}
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);
free(lease->ntp);
lease->ntp_count = 0;
lease->ntp_allocated = 0;
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 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);
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);
@ -185,6 +372,13 @@ sd_dhcp6_lease *sd_dhcp6_lease_unref(sd_dhcp6_lease *lease) {
free(lease->serverid);
dhcp6_lease_free_ia(&lease->ia);
free(lease->dns);
lease->domains = strv_free(lease->domains);
free(lease->ntp);
lease->ntp_fqdn = strv_free(lease->ntp_fqdn);
free(lease);
}

View File

@ -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);
@ -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);

View File

@ -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;
}
}
}

View File

@ -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,12 @@ 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);
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);