From 1e7f82f4d8d86ee3d5412ae15e61c1cdad621b4c Mon Sep 17 00:00:00 2001 From: Simon Kelley Date: Thu, 1 May 2014 16:37:43 +0100 Subject: First commit for DHCPv6_PD. --- man/dnsmasq.8 | 14 +- src/config.h | 9 ++ src/dhcp-common.c | 8 + src/dhcp6-protocol.h | 3 + src/dhcp6.c | 15 ++ src/dnsmasq.c | 29 ++-- src/dnsmasq.h | 22 ++- src/helper.c | 82 ++++++++--- src/lease.c | 99 ++++++++++--- src/option.c | 21 ++- src/rfc3315.c | 408 ++++++++++++++++++++++++++++++++++++++++++++++----- 11 files changed, 619 insertions(+), 91 deletions(-) diff --git a/man/dnsmasq.8 b/man/dnsmasq.8 index 6658f16..cfc8e81 100644 --- a/man/dnsmasq.8 +++ b/man/dnsmasq.8 @@ -828,7 +828,10 @@ can be combined with .B ra-stateless and .B slaac. - +.TP +.B \-F, --dhcp-prefix=[tag:[,tag:],],[,] +Enable the DHCP server and define a prefix for DHCPv6 prefix-delegation. Dnsmasq must be compiled with +support for DHCPv6-PD. The tag system may be used to define prefixes for individual hosts or classes of hosts. .TP .B \-G, --dhcp-host=[][,id:|*][,set:][,][,][,][,ignore] Specify per host parameters for the DHCP server. This allows a machine @@ -1372,6 +1375,8 @@ if known. "add" means a lease has been created, "del" means it has been destroyed, "old" is a notification of an existing lease when dnsmasq starts or a change to MAC address or hostname of an existing lease (also, lease length or expiry and client-id, if leasefile-ro is set). +These arguments have are changed to "add-prefix", "old-prefix" etc when the lease is +for a prefix delegation. If the MAC address is from a network type other than ethernet, it will have the network type prepended, eg "06-01:23:45:67:89:ab" for token ring. The process is run as root (assuming that dnsmasq was originally run as @@ -1439,6 +1444,13 @@ temporary allocation, this is prefixed to 'T'. DNSMASQ_MAC containing the MAC address of the client, if known. +For DHCPv6-PD only + +DNSMASQ_CLIENT_ADDRESS contains the link-local address of the client which has +been leased the prefix + +DNSMASQ_PREFIX_LENGTH contains the prefix length of the leased prefix. + Note that the supplied hostname, vendorclass and userclass data is only supplied for "add" actions or "old" actions when a host resumes an existing lease, diff --git a/src/config.h b/src/config.h index 1793e40..52ca311 100644 --- a/src/config.h +++ b/src/config.h @@ -137,6 +137,7 @@ RESOLVFILE #define HAVE_DHCP #define HAVE_DHCP6 +#define HAVE_PD #define HAVE_TFTP #define HAVE_SCRIPT #define HAVE_AUTH @@ -311,6 +312,10 @@ HAVE_SOCKADDR_SA_LEN #undef HAVE_DHCP6 #endif +#if defined(NO_PD) || !defined(HAVE_DHCP6) +#undef HAVE_PD +#endif + /* DHCP6 needs DHCP too */ #ifdef HAVE_DHCP6 #define HAVE_DHCP @@ -375,6 +380,10 @@ static char *compile_opts = "no-" # endif "DHCPv6 " +# if !defined (HAVE_PD) + "no-" +# endif + "DHCPv6-PD " # if !defined(HAVE_SCRIPT) "no-scripts " # else diff --git a/src/dhcp-common.c b/src/dhcp-common.c index 9d13ac8..05f3ee6 100644 --- a/src/dhcp-common.c +++ b/src/dhcp-common.c @@ -890,6 +890,14 @@ void log_context(int family, struct dhcp_context *context) #endif } + +#ifdef HAVE_PD +void log_prefix(struct dhcp_context *context) +{ + inet_ntop(AF_INET6, &context->start6, daemon->addrbuff, ADDRSTRLEN); + my_syslog(MS_DHCP | LOG_INFO, _("DHCPv6-PD with prefix %s/%d"), daemon->addrbuff, context->prefix); +} +#endif void log_relay(int family, struct dhcp_relay *relay) { diff --git a/src/dhcp6-protocol.h b/src/dhcp6-protocol.h index 5927dc3..ff88eb9 100644 --- a/src/dhcp6-protocol.h +++ b/src/dhcp6-protocol.h @@ -55,6 +55,8 @@ #define OPTION6_RECONF_ACCEPT 20 #define OPTION6_DNS_SERVER 23 #define OPTION6_DOMAIN_SEARCH 24 +#define OPTION6_IA_PD 25 +#define OPTION6_IAPREFIX 26 #define OPTION6_REFRESH_TIME 32 #define OPTION6_REMOTE_ID 37 #define OPTION6_SUBSCRIBER_ID 38 @@ -72,4 +74,5 @@ #define DHCP6NOBINDING 3 #define DHCP6NOTONLINK 4 #define DHCP6USEMULTI 5 +#define DHCP6NOPREFIX 6 diff --git a/src/dhcp6.c b/src/dhcp6.c index 3a83c18..03a2087 100644 --- a/src/dhcp6.c +++ b/src/dhcp6.c @@ -774,6 +774,21 @@ void dhcp_construct_contexts(time_t now) } } +#ifdef HAVE_PD +struct dhcp_context *find_prefix_context(struct dhcp_netid *tags, struct in6_addr *addr, int prefix_len) +{ + struct dhcp_context *tmp; + + for (tmp = daemon->prefix_contexts; tmp; tmp = tmp->current) + if (prefix_len == tmp->prefix && + IN6_ARE_ADDR_EQUAL(&tmp->start6, addr) && + match_netid(tmp->filter, tags, 1)) + return tmp; + + return NULL; +} +#endif + #endif diff --git a/src/dnsmasq.c b/src/dnsmasq.c index 1c96a0e..efed830 100644 --- a/src/dnsmasq.c +++ b/src/dnsmasq.c @@ -104,14 +104,6 @@ int main (int argc, char **argv) } #endif -#ifdef HAVE_DHCP - if (!daemon->lease_file) - { - if (daemon->dhcp || daemon->dhcp6) - daemon->lease_file = LEASEFILE; - } -#endif - /* Close any file descriptors we inherited apart from std{in|out|err} Ensure that at least stdin, stdout and stderr (fd 0, 1, 2) exist, @@ -198,6 +190,12 @@ int main (int argc, char **argv) #endif #ifdef HAVE_DHCP6 + +#ifdef HAVE_PD + if (daemon->prefix_contexts) + daemon->doing_dhcp6 = 1; +#endif + if (daemon->dhcp6) { daemon->doing_ra = option_bool(OPT_RA); @@ -217,6 +215,12 @@ int main (int argc, char **argv) #endif #ifdef HAVE_DHCP + if (!daemon->lease_file) + { + if (daemon->dhcp || daemon->doing_dhcp6) + daemon->lease_file = LEASEFILE; + } + /* Note that order matters here, we must call lease_init before creating any file descriptors which shouldn't be leaked to the lease-script init process. We need to call common_init @@ -332,7 +336,7 @@ int main (int argc, char **argv) #if defined(HAVE_SCRIPT) /* Note getpwnam returns static storage */ - if ((daemon->dhcp || daemon->dhcp6) && + if ((daemon->dhcp || daemon->doing_dhcp6) && daemon->scriptuser && (daemon->lease_change_command || daemon->luascript)) { @@ -518,7 +522,7 @@ int main (int argc, char **argv) /* if we are to run scripts, we need to fork a helper before dropping root. */ daemon->helperfd = -1; #ifdef HAVE_SCRIPT - if ((daemon->dhcp || daemon->dhcp6) && (daemon->lease_change_command || daemon->luascript)) + if ((daemon->dhcp || daemon->doing_dhcp6) && (daemon->lease_change_command || daemon->luascript)) daemon->helperfd = create_helper(pipewrite, err_pipe[1], script_uid, script_gid, max_fd); #endif @@ -715,6 +719,11 @@ int main (int argc, char **argv) for (context = daemon->dhcp6; context; context = context->next) log_context(AF_INET6, context); +#ifdef HAVE_PD + for (context = daemon->prefix_contexts; context; context = context->next) + log_prefix(context); +#endif + for (relay = daemon->relay6; relay; relay = relay->next) log_relay(AF_INET6, relay); diff --git a/src/dnsmasq.h b/src/dnsmasq.h index 3032546..4270e6a 100644 --- a/src/dnsmasq.h +++ b/src/dnsmasq.h @@ -622,6 +622,7 @@ struct frec { #define LEASE_NA 32 /* IPv6 no-temporary lease */ #define LEASE_TA 64 /* IPv6 temporary lease */ #define LEASE_HAVE_HWADDR 128 /* Have set hwaddress */ +#define LEASE_PD 256 /* Prefix delegation */ struct dhcp_lease { int clid_len; /* length of client identifier */ @@ -641,6 +642,10 @@ struct dhcp_lease { int last_interface; #ifdef HAVE_DHCP6 struct in6_addr addr6; + int prefix_len; /* for PD only */ +#ifdef HAVE_PD + struct in6_addr client_addr; +#endif int iaid; struct slaac_address { struct in6_addr addr; @@ -837,7 +842,7 @@ struct dhcp_context { #define CONTEXT_USED (1u<<15) #define CONTEXT_OLD (1u<<16) #define CONTEXT_V6 (1u<<17) - +#define CONTEXT_PREFIX (1u<<18) struct ping_result { struct in_addr addr; @@ -926,7 +931,7 @@ extern struct daemon { int port, query_port, min_port; unsigned long local_ttl, neg_ttl, max_ttl, max_cache_ttl, auth_ttl; struct hostsfile *addn_hosts; - struct dhcp_context *dhcp, *dhcp6; + struct dhcp_context *dhcp, *dhcp6, *prefix_contexts; struct ra_interface *ra_interfaces; struct dhcp_config *dhcp_conf; struct dhcp_opt *dhcp_opts, *dhcp_match, *dhcp_opts6, *dhcp_match6; @@ -1244,7 +1249,7 @@ void lease_update_dns(int force); void lease_init(time_t now); struct dhcp_lease *lease4_allocate(struct in_addr addr); #ifdef HAVE_DHCP6 -struct dhcp_lease *lease6_allocate(struct in6_addr *addrp, int lease_type); +struct dhcp_lease *lease6_allocate(struct in6_addr *addrp, int prefix_len, int lease_type); struct dhcp_lease *lease6_find(unsigned char *clid, int clid_len, int lease_type, int iaid, struct in6_addr *addr); void lease6_reset(void); @@ -1274,6 +1279,9 @@ void lease_find_interfaces(time_t now); void lease_add_extradata(struct dhcp_lease *lease, unsigned char *data, unsigned int len, int delim); #endif +#ifdef HAVE_PD +struct dhcp_lease *lease6_find_by_prefix(struct in6_addr *addr, int prefix_len); +#endif #endif /* rfc2131.c */ @@ -1374,6 +1382,10 @@ void make_duid(time_t now); void dhcp_construct_contexts(time_t now); void get_client_mac(struct in6_addr *client, int iface, unsigned char *mac, unsigned int *maclenp, unsigned int *mactypep); +#ifdef HAVE_PD +struct dhcp_context *find_prefix_context(struct dhcp_netid *tags, struct in6_addr *addr, int prefix_len); +#endif + #endif /* rfc3315.c */ @@ -1418,6 +1430,10 @@ void display_opts6(void); # endif void log_context(int family, struct dhcp_context *context); void log_relay(int family, struct dhcp_relay *relay); +#ifdef HAVE_PD +void log_prefix(struct dhcp_context *context); +#endif + #endif /* outpacket.c */ diff --git a/src/helper.c b/src/helper.c index 4be53c3..01f2728 100644 --- a/src/helper.c +++ b/src/helper.c @@ -69,6 +69,10 @@ struct script_data #endif #ifdef HAVE_DHCP6 int iaid, vendorclass_count; +#ifdef HAVE_PD + int prefix_len; + struct in6_addr client_addr; +#endif #endif unsigned char hwaddr[DHCP_CHADDR_MAX]; char interface[IF_NAMESIZE]; @@ -206,22 +210,37 @@ int create_helper(int event_fd, int err_fd, uid_t uid, gid_t gid, long max_fd) _exit(0); } - is6 = !!(data.flags & (LEASE_TA | LEASE_NA)); + is6 = !!(data.flags & (LEASE_TA | LEASE_NA | LEASE_PD)); - if (data.action == ACTION_DEL) - action_str = "del"; - else if (data.action == ACTION_ADD) - action_str = "add"; - else if (data.action == ACTION_OLD || data.action == ACTION_OLD_HOSTNAME) - action_str = "old"; - else if (data.action == ACTION_TFTP) +#ifdef HAVE_PD + if (data.flags & LEASE_PD) { - action_str = "tftp"; - is6 = (data.flags != AF_INET); + if (data.action == ACTION_DEL) + action_str = "del-prefix"; + else if (data.action == ACTION_ADD) + action_str = "add-prefix"; + else if (data.action == ACTION_OLD || data.action == ACTION_OLD_HOSTNAME) + action_str = "old-prefix"; + else + continue; } else - continue; - +#endif + { + if (data.action == ACTION_DEL) + action_str = "del"; + else if (data.action == ACTION_ADD) + action_str = "add"; + else if (data.action == ACTION_OLD || data.action == ACTION_OLD_HOSTNAME) + action_str = "old"; + else if (data.action == ACTION_TFTP) + { + action_str = "tftp"; + is6 = (data.flags != AF_INET); + } + else + continue; + } /* stringify MAC into dhcp_buff */ p = daemon->dhcp_buff; @@ -375,14 +394,27 @@ int create_helper(int event_fd, int err_fd, uid_t uid, gid_t gid, long max_fd) if (!is6) buf = grab_extradata_lua(buf, end, "vendor_class"); #ifdef HAVE_DHCP6 - else if (data.vendorclass_count != 0) + else { - sprintf(daemon->dhcp_buff2, "vendor_class_id"); - buf = grab_extradata_lua(buf, end, daemon->dhcp_buff2); - for (i = 0; i < data.vendorclass_count - 1; i++) +#ifdef HAVE_PD + if (data.flags & LEASE_PD) + { + inet_ntop(AF_INET6, &data.client_addr, daemon->dhcp_buff2, ADDRSTRLEN); + lua_pushstring(lua, daemon->dhcp_buff2); + lua_setfield(lua, -2, "client_address"); + lua_pushnumber(lua, data.prefix_len); + lua_setfield(lua, -2, "prefix_length"); + } +#endif + if (data.vendorclass_count != 0) { - sprintf(daemon->dhcp_buff2, "vendor_class%i", i); + sprintf(daemon->dhcp_buff2, "vendor_class_id"); buf = grab_extradata_lua(buf, end, daemon->dhcp_buff2); + for (i = 0; i < data.vendorclass_count - 1; i++) + { + sprintf(daemon->dhcp_buff2, "vendor_class%i", i); + buf = grab_extradata_lua(buf, end, daemon->dhcp_buff2); + } } } #endif @@ -498,7 +530,17 @@ int create_helper(int event_fd, int err_fd, uid_t uid, gid_t gid, long max_fd) #endif my_setenv("DNSMASQ_DOMAIN", domain, &err); - + +#ifdef HAVE_PD + if (is6 && (data.flags & LEASE_PD)) + { + inet_ntop(AF_INET6, &data.client_addr, daemon->dhcp_buff2, ADDRSTRLEN); + my_setenv("DNSMASQ_CLIENT_ADDRESS", daemon->dhcp_buff2, &err); + sprintf(daemon->dhcp_buff2, "%u", data.prefix_len); + my_setenv("DNSMASQ_PREFIX_LENGTH", daemon->dhcp_buff2, &err); + } +#endif + end = extradata + data.ed_len; buf = extradata; @@ -687,6 +729,10 @@ void queue_script(int action, struct dhcp_lease *lease, char *hostname, time_t n buf->vendorclass_count = lease->vendorclass_count; buf->addr6 = lease->addr6; buf->iaid = lease->iaid; +#ifdef HAVE_PD + buf->prefix_len = lease->prefix_len; + buf->client_addr = lease->client_addr; +#endif #endif buf->hwaddr_len = lease->hwaddr_len; buf->hwaddr_type = lease->hwaddr_type; diff --git a/src/lease.c b/src/lease.c index 5d1eefc..1ce7dbb 100644 --- a/src/lease.c +++ b/src/lease.c @@ -26,8 +26,11 @@ void lease_init(time_t now) unsigned long ei; struct all_addr addr; struct dhcp_lease *lease; - int clid_len, hw_len, hw_type; + int clid_len, hw_len, hw_type, prefix_len = 0; FILE *leasestream; + char *cp; + + (void)cp; leases_left = daemon->dhcp_max; @@ -50,7 +53,6 @@ void lease_init(time_t now) file_dirty = dns_dirty = 0; return; } - } else { @@ -89,6 +91,14 @@ void lease_init(time_t now) if (strcmp(daemon->packet, "*") != 0) clid_len = parse_hex(daemon->packet, (unsigned char *)daemon->packet, 255, NULL, NULL); +#ifdef HAVE_PD + if ((cp = strchr(daemon->namebuff, '/'))) + { + *cp = 0; + prefix_len = atoi(cp+1); + } +#endif + if (inet_pton(AF_INET, daemon->namebuff, &addr.addr.addr4) && (lease = lease4_allocate(addr.addr.addr4))) { @@ -115,15 +125,27 @@ void lease_init(time_t now) lease_type = LEASE_TA; s++; } +#ifdef HAVE_PD + else if (s[0] == 'P') + { + lease_type = LEASE_PD; + s++; + } +#endif iaid = strtoul(s, NULL, 10); - if ((lease = lease6_allocate(&addr.addr.addr6, lease_type))) + if ((lease = lease6_allocate(&addr.addr.addr6, prefix_len, lease_type))) { lease_set_hwaddr(lease, NULL, (unsigned char *)daemon->packet, 0, 0, clid_len, now, 0); lease_set_iaid(lease, iaid); - if (strcmp(daemon->dhcp_buff, "*") != 0) - lease_set_hostname(lease, daemon->dhcp_buff, 0, get_domain6((struct in6_addr *)lease->hwaddr), NULL); +#ifdef HAVE_PD + if (lease_type == LEASE_PD) + inet_pton(AF_INET6, daemon->dhcp_buff, &lease->client_addr); + else +#endif + if (strcmp(daemon->dhcp_buff, "*") != 0) + lease_set_hostname(lease, daemon->dhcp_buff, 0, get_domain6((struct in6_addr *)lease->hwaddr), NULL); } } #endif @@ -188,7 +210,7 @@ void lease_update_from_configs(void) char *name; for (lease = leases; lease; lease = lease->next) - if (lease->flags & (LEASE_TA | LEASE_NA)) + if (lease->flags & (LEASE_TA | LEASE_NA | LEASE_PD)) continue; else if ((config = find_config(daemon->dhcp_conf, NULL, lease->clid, lease->clid_len, lease->hwaddr, lease->hwaddr_len, lease->hwaddr_type, NULL)) && @@ -226,7 +248,7 @@ void lease_update_file(time_t now) { #ifdef HAVE_DHCP6 - if (lease->flags & (LEASE_TA | LEASE_NA)) + if (lease->flags & (LEASE_TA | LEASE_NA | LEASE_PD)) continue; #endif @@ -271,7 +293,7 @@ void lease_update_file(time_t now) for (lease = leases; lease; lease = lease->next) { - if (!(lease->flags & (LEASE_TA | LEASE_NA))) + if (!(lease->flags & (LEASE_TA | LEASE_NA | LEASE_PD))) continue; #ifdef HAVE_BROKEN_RTC @@ -282,10 +304,21 @@ void lease_update_file(time_t now) inet_ntop(AF_INET6, &lease->addr6, daemon->addrbuff, ADDRSTRLEN); - ourprintf(&err, "%s%u %s ", (lease->flags & LEASE_TA) ? "T" : "", - lease->iaid, daemon->addrbuff); - ourprintf(&err, "%s ", lease->hostname ? lease->hostname : "*"); - +#ifdef HAVE_PD + if (lease->flags & LEASE_PD) + { + ourprintf(&err, "P%u %s/%d ", lease->iaid, daemon->addrbuff, lease->prefix_len); + inet_ntop(AF_INET6, &lease->client_addr, daemon->addrbuff, ADDRSTRLEN); + ourprintf(&err, "%s ", daemon->addrbuff); + } + else +#endif + { + ourprintf(&err, "%s%u %s ", (lease->flags & LEASE_TA) ? "T" : "", + lease->iaid, daemon->addrbuff); + ourprintf(&err, "%s ", lease->hostname ? lease->hostname : "*"); + } + if (lease->clid && lease->clid_len != 0) { for (i = 0; i < lease->clid_len - 1; i++) @@ -358,7 +391,7 @@ static int find_interface_v4(struct in_addr local, int if_index, char *label, (void) vparam; for (lease = leases; lease; lease = lease->next) - if (!(lease->flags & (LEASE_TA | LEASE_NA))) + if (!(lease->flags & (LEASE_TA | LEASE_NA | LEASE_PD))) if (is_same_net(local, lease->addr, netmask)) lease_set_interface(lease, if_index, *((time_t *)vparam)); @@ -451,7 +484,12 @@ void lease_update_dns(int force) for (lease = leases; lease; lease = lease->next) { int prot = AF_INET; - + +#ifdef HAVE_PD + if (lease->flags & LEASE_PD) + continue; +#endif + #ifdef HAVE_DHCP6 if (lease->flags & (LEASE_TA | LEASE_NA)) prot = AF_INET6; @@ -529,7 +567,7 @@ struct dhcp_lease *lease_find_by_client(unsigned char *hwaddr, int hw_len, int h for (lease = leases; lease; lease = lease->next) { #ifdef HAVE_DHCP6 - if (lease->flags & (LEASE_TA | LEASE_NA)) + if (lease->flags & (LEASE_TA | LEASE_NA | LEASE_PD)) continue; #endif if (lease->clid && clid_len == lease->clid_len && @@ -540,7 +578,7 @@ struct dhcp_lease *lease_find_by_client(unsigned char *hwaddr, int hw_len, int h for (lease = leases; lease; lease = lease->next) { #ifdef HAVE_DHCP6 - if (lease->flags & (LEASE_TA | LEASE_NA)) + if (lease->flags & (LEASE_TA | LEASE_NA | LEASE_PD)) continue; #endif if ((!lease->clid || !clid) && @@ -561,7 +599,7 @@ struct dhcp_lease *lease_find_by_addr(struct in_addr addr) for (lease = leases; lease; lease = lease->next) { #ifdef HAVE_DHCP6 - if (lease->flags & (LEASE_TA | LEASE_NA)) + if (lease->flags & (LEASE_TA | LEASE_NA | LEASE_PD)) continue; #endif if (lease->addr.s_addr == addr.s_addr) @@ -650,6 +688,25 @@ struct dhcp_lease *lease6_find_by_addr(struct in6_addr *net, int prefix, u64 add return NULL; } +#ifdef HAVE_PD +struct dhcp_lease *lease6_find_by_prefix(struct in6_addr *addr, int prefix_len) +{ + struct dhcp_lease *lease; + + for (lease = leases; lease; lease = lease->next) + { + if (!(lease->flags & LEASE_PD)) + continue; + + if (prefix_len == lease->prefix_len && + IN6_ARE_ADDR_EQUAL(&lease->addr6, addr)) + return lease; + } + + return NULL; +} +#endif + /* Find largest assigned address in context */ u64 lease_find_max_addr6(struct dhcp_context *context) { @@ -728,13 +785,14 @@ struct dhcp_lease *lease4_allocate(struct in_addr addr) } #ifdef HAVE_DHCP6 -struct dhcp_lease *lease6_allocate(struct in6_addr *addrp, int lease_type) +struct dhcp_lease *lease6_allocate(struct in6_addr *addrp, int prefix_len, int lease_type) { struct dhcp_lease *lease = lease_allocate(); if (lease) { lease->addr6 = *addrp; + lease->prefix_len = prefix_len; lease->flags |= lease_type; lease->iaid = 0; } @@ -919,6 +977,11 @@ void lease_set_hostname(struct dhcp_lease *lease, char *name, int auth, char *do /* Depending on mode, we check either unqualified name or FQDN. */ for (lease_tmp = leases; lease_tmp; lease_tmp = lease_tmp->next) { +#ifdef HAVE_PD + if (lease->flags & LEASE_PD) + continue; +#endif + if (option_bool(OPT_DHCP_FQDN)) { if (!new_fqdn || !lease_tmp->fqdn || !hostname_isequal(lease_tmp->fqdn, new_fqdn)) diff --git a/src/option.c b/src/option.c index daa728f..fb4b3d4 100644 --- a/src/option.c +++ b/src/option.c @@ -146,6 +146,7 @@ struct myoption { #define LOPT_DNSSEC_CHECK 334 #define LOPT_LOCAL_SERVICE 335 #define LOPT_DNSSEC_TIME 336 +#define LOPT_DHCP_PREFIX 337 #ifdef HAVE_GETOPT_LONG static const struct option opts[] = @@ -297,6 +298,7 @@ static const struct myoption opts[] = { "quiet-dhcp", 0, 0, LOPT_QUIET_DHCP }, { "quiet-dhcp6", 0, 0, LOPT_QUIET_DHCP6 }, { "quiet-ra", 0, 0, LOPT_QUIET_RA }, + { "dhcp-prefix", 1, 0, LOPT_DHCP_PREFIX }, { NULL, 0, 0, 0 } }; @@ -325,6 +327,7 @@ static struct { { 'E', OPT_EXPAND, NULL, gettext_noop("Expand simple names in /etc/hosts with domain-suffix."), NULL }, { 'f', OPT_FILTER, NULL, gettext_noop("Don't forward spurious DNS requests from Windows hosts."), NULL }, { 'F', ARG_DUP, ",...", gettext_noop("Enable DHCP in the range given with lease duration."), NULL }, + { LOPT_DHCP_PREFIX, ARG_DUP, ",...", gettext_noop("Enable DHCP-PD for given prefix with lease duration."), NULL }, { 'g', ARG_ONE, "", gettext_noop("Change to this group after startup (defaults to %s)."), CHGRP }, { 'G', ARG_DUP, "", gettext_noop("Set address or hostname for a specified machine."), NULL }, { LOPT_DHCP_HOST, ARG_DUP, "", gettext_noop("Read DHCP host specs from file."), NULL }, @@ -2476,6 +2479,9 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma #ifdef HAVE_DHCP case 'F': /* --dhcp-range */ +#ifdef HAVE_PD + case LOPT_DHCP_PREFIX: /* --dhcp-prefix */ +#endif { int k, leasepos = 2; char *cp, *a[8] = { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL }; @@ -2536,6 +2542,8 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma if (inet_pton(AF_INET, a[0], &new->start)) { + if (option == LOPT_DHCP_PREFIX) + ret_err(_("bad dhcp-prefix")); new->next = daemon->dhcp; daemon->dhcp = new; new->end = new->start; @@ -2575,8 +2583,17 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma new->flags |= CONTEXT_V6; new->prefix = 64; /* default */ new->end6 = new->start6; - new->next = daemon->dhcp6; - daemon->dhcp6 = new; + if (option == LOPT_DHCP_PREFIX) + { + new->flags |= CONTEXT_PREFIX; + new->next = daemon->prefix_contexts; + daemon->prefix_contexts = new; + } + else + { + new->next = daemon->dhcp6; + daemon->dhcp6 = new; + } for (leasepos = 1; leasepos < k; leasepos++) { diff --git a/src/rfc3315.c b/src/rfc3315.c index 364277d..70768a2 100644 --- a/src/rfc3315.c +++ b/src/rfc3315.c @@ -24,7 +24,7 @@ struct state { int clid_len, iaid, ia_type, interface, hostname_auth, lease_allocate; char *client_hostname, *hostname, *domain, *send_domain; struct dhcp_context *context; - struct in6_addr *link_address, *fallback, *ll_addr, *ula_addr; + struct in6_addr *link_address, peer_address, *fallback, *ll_addr, *ula_addr; unsigned int xid, fqdn_flags; char *iface_name; void *packet_options, *end; @@ -57,11 +57,16 @@ static void mark_config_used(struct dhcp_context *context, struct in6_addr *addr static int check_address(struct state *state, struct in6_addr *addr); static void add_address(struct state *state, struct dhcp_context *context, unsigned int lease_time, void *ia_option, unsigned int *min_time, struct in6_addr *addr, time_t now); -static void update_leases(struct state *state, struct dhcp_context *context, struct in6_addr *addr, unsigned int lease_time, time_t now); +static struct dhcp_lease *update_leases(struct state *state, struct dhcp_context *context, struct dhcp_lease *lease, + struct in6_addr *addr, int prefix_len, unsigned int lease_time, time_t now); static int add_local_addrs(struct dhcp_context *context); static struct dhcp_netid *add_options(struct state *state, int do_refresh); +static int check_context(struct state *state); static void calculate_times(struct dhcp_context *context, unsigned int *min_time, unsigned int *valid_timep, unsigned int *preferred_timep, unsigned int lease_time); +#ifdef HAVE_PD +static int handle_pd(int msg_type, struct state *state, struct dhcp_netid *tags, void *ia_option, void *ia_end, time_t now); +#endif #define opt6_len(opt) ((int)(opt6_uint(opt, -2, 2))) #define opt6_type(opt) (opt6_uint(opt, -4, 2)) @@ -99,7 +104,9 @@ unsigned short dhcp6_reply(struct dhcp_context *context, int interface, char *if state.mac_len = 0; state.tags = NULL; state.link_address = NULL; - + + memcpy(&state.peer_address, client_addr, IN6ADDRSZ); + if (dhcp6_maybe_relay(&state, daemon->dhcp_packet.iov_base, sz, client_addr, IN6_IS_ADDR_MULTICAST(client_addr), now)) return msg_type == DHCP6RELAYFORW ? DHCPV6_SERVER_PORT : DHCPV6_CLIENT_PORT; @@ -150,7 +157,7 @@ static int dhcp6_maybe_relay(struct state *state, void *inbuff, size_t sz, state->context = c; } - if (!state->context) + if (0 && !state->context) /* TODO */ { inet_ntop(AF_INET6, state->link_address, daemon->addrbuff, ADDRSTRLEN); my_syslog(MS_DHCP | LOG_WARNING, @@ -160,7 +167,7 @@ static int dhcp6_maybe_relay(struct state *state, void *inbuff, size_t sz, } } - if (!state->context) + if (0 && !state->context) /* TODO */ { my_syslog(MS_DHCP | LOG_WARNING, _("no address range available for DHCPv6 request via %s"), state->iface_name); @@ -220,6 +227,7 @@ static int dhcp6_maybe_relay(struct state *state, void *inbuff, size_t sz, /* the packet data is unaligned, copy to aligned storage */ memcpy(&align, inbuff + 2, IN6ADDRSZ); state->link_address = &align; + memcpy(&state->peer_address, inbuff + 18, IN6ADDRSZ); /* zero is_unicast since that is now known to refer to the relayed packet, not the original sent by the client */ if (!dhcp6_maybe_relay(state, opt6_ptr(opt, 0), opt6_len(opt), client_addr, 0, now)) @@ -314,7 +322,7 @@ static int dhcp6_no_relay(struct state *state, int msg_type, void *inbuff, size_ return 0; /* server-id must match except for SOLICIT and CONFIRM messages */ - if (msg_type != DHCP6SOLICIT && msg_type != DHCP6CONFIRM && msg_type != DHCP6IREQ && + if (msg_type != DHCP6SOLICIT && msg_type != DHCP6CONFIRM && msg_type != DHCP6IREQ && msg_type != DHCP6REBIND && (!(opt = opt6_find(state->packet_options, state->end, OPTION6_SERVER_ID, 1)) || opt6_len(opt) != daemon->duid_len || memcmp(opt6_ptr(opt, 0), daemon->duid, daemon->duid_len) != 0)) @@ -630,7 +638,18 @@ static int dhcp6_no_relay(struct state *state, int msg_type, void *inbuff, size_ if (!check_ia(state, opt, &ia_end, &ia_option)) continue; + +#ifdef HAVE_PD + if (handle_pd(msg_type, state, tagif, ia_option, ia_end, now)) + { + address_assigned = 1; + continue; + } +#endif + if (!check_context(state)) + return 0; + /* reset USED bits in contexts - one address per prefix per IAID */ for (c = state->context; c; c = c->current) c->flags &= ~CONTEXT_USED; @@ -831,19 +850,29 @@ static int dhcp6_no_relay(struct state *state, int msg_type, void *inbuff, size_ unsigned int min_time = 0xffffffff; int t1cntr; - if (!check_ia(state, opt, &ia_end, &ia_option)) - continue; - - if (!ia_option) - { - /* If we get a request with a IA_*A without addresses, treat it exactly like - a SOLICT with rapid commit set. */ - save_counter(start); - goto request_no_address; - } + if (!check_ia(state, opt, &ia_end, &ia_option)) + continue; + + if (!ia_option) + { + /* If we get a request with a IA_*A without addresses, treat it exactly like + a SOLICT with rapid commit set. */ + save_counter(start); + goto request_no_address; + } + +#ifdef HAVE_PD + if (handle_pd(msg_type, state, tagif, ia_option, ia_end, now)) + { + address_assigned = 1; + continue; + } +#endif + if (!check_context(state)) + return 0; o = build_ia(state, &t1cntr); - + for (; ia_option; ia_option = opt6_find(opt6_next(ia_option, ia_end), ia_end, OPTION6_IAADDR, 24)) { struct in6_addr *req_addr = opt6_ptr(ia_option, 0); @@ -929,11 +958,12 @@ static int dhcp6_no_relay(struct state *state, int msg_type, void *inbuff, size_ case DHCP6RENEW: + case DHCP6REBIND: { /* set reply message type */ *outmsgtypep = DHCP6REPLY; - log6_quiet(state, "DHCPRENEW", NULL, NULL); + log6_quiet(state, msg_type == DHCP6RENEW ? "DHCPRENEW" : "DHCPREBIND", NULL, NULL); for (opt = state->packet_options; opt; opt = opt6_next(opt, state->end)) { @@ -944,6 +974,14 @@ static int dhcp6_no_relay(struct state *state, int msg_type, void *inbuff, size_ if (!check_ia(state, opt, &ia_end, &ia_option)) continue; +#ifdef HAVE_PD + if (handle_pd(msg_type, state, tagif, ia_option, ia_end, now)) + continue; +#endif + + if (!check_context(state)) + return 0; + o = build_ia(state, &t1cntr); iacntr = save_counter(-1); @@ -1044,6 +1082,9 @@ static int dhcp6_no_relay(struct state *state, int msg_type, void *inbuff, size_ log6_quiet(state, "DHCPCONFIRM", NULL, NULL); + if (!check_context(state)) + return 0; + for (opt = state->packet_options; opt; opt = opt6_next(opt, state->end)) { void *ia_option, *ia_end; @@ -1107,12 +1148,20 @@ static int dhcp6_no_relay(struct state *state, int msg_type, void *inbuff, size_ *outmsgtypep = DHCP6REPLY; log6_quiet(state, "DHCPRELEASE", NULL, NULL); - + + if (!check_context(state)) + return 0; + for (opt = state->packet_options; opt; opt = opt6_next(opt, state->end)) { void *ia_option, *ia_end; int made_ia = 0; - + +#ifdef HAVE_PD + if (handle_pd(msg_type, state, tagif, ia_option, ia_end, now)) + continue; +#endif + for (check_ia(state, opt, &ia_end, &ia_option); ia_option; ia_option = opt6_find(opt6_next(ia_option, ia_end), ia_end, OPTION6_IAADDR, 24)) @@ -1169,6 +1218,9 @@ static int dhcp6_no_relay(struct state *state, int msg_type, void *inbuff, size_ *outmsgtypep = DHCP6REPLY; log6_quiet(state, "DHCPDECLINE", NULL, NULL); + + if (!check_context(state)) + return 0; for (opt = state->packet_options; opt; opt = opt6_next(opt, state->end)) { @@ -1525,20 +1577,20 @@ static int check_ia(struct state *state, void *opt, void **endp, void **ia_optio { state->ia_type = opt6_type(opt); *ia_option = NULL; - - if (state->ia_type != OPTION6_IA_NA && state->ia_type != OPTION6_IA_TA) - return 0; + *endp = opt6_ptr(opt, opt6_len(opt)); - if (state->ia_type == OPTION6_IA_NA && opt6_len(opt) < 12) - return 0; - - if (state->ia_type == OPTION6_IA_TA && opt6_len(opt) < 4) + if (state->ia_type == OPTION6_IA_NA && opt6_len(opt) >= 12) + *ia_option = opt6_find(opt6_ptr(opt, 12), *endp, OPTION6_IAADDR, 24); + else if (state->ia_type == OPTION6_IA_TA && opt6_len(opt) >= 4) + *ia_option = opt6_find(opt6_ptr(opt, 4), *endp, OPTION6_IAADDR, 24); +#ifdef HAVE_PD + else if (state->ia_type == OPTION6_IA_PD && opt6_len(opt) >= 12) + *ia_option = opt6_find(opt6_ptr(opt, 12), *endp, OPTION6_IAPREFIX, 25); +#endif + else return 0; - *endp = opt6_ptr(opt, opt6_len(opt)); state->iaid = opt6_uint(opt, 0, 4); - *ia_option = opt6_find(opt6_ptr(opt, state->ia_type == OPTION6_IA_NA ? 12 : 4), *endp, OPTION6_IAADDR, 24); - return 1; } @@ -1550,7 +1602,7 @@ static int build_ia(struct state *state, int *t1cntr) put_opt6_long(state->iaid); *t1cntr = 0; - if (state->ia_type == OPTION6_IA_NA) + if (state->ia_type == OPTION6_IA_NA || state->ia_type == OPTION6_IA_PD) { /* save pointer */ *t1cntr = save_counter(-1); @@ -1617,10 +1669,12 @@ static void add_address(struct state *state, struct dhcp_context *context, unsig end_opt6(o); + lease = lease6_find_by_addr(addr, 128, 0); + if (state->lease_allocate) - update_leases(state, context, addr, valid_time, now); + lease = update_leases(state, context, lease, addr, 0, valid_time, now); - if ((lease = lease6_find_by_addr(addr, 128, 0))) + if (lease) lease->flags |= LEASE_USED; /* get tags from context if we've not used it before */ @@ -1735,8 +1789,9 @@ static void calculate_times(struct dhcp_context *context, unsigned int *min_time } /* deprecate (preferred == 0) which configured, or when local address - is deprecated */ - if ((context->flags & CONTEXT_DEPRECATE) || context->preferred == 0) + is deprecated, not valid for PD */ + if (!(context->flags & CONTEXT_PREFIX) && + ((context->flags & CONTEXT_DEPRECATE) || context->preferred == 0)) preferred_time = 0; if (preferred_time != 0 && preferred_time < *min_time) @@ -1749,9 +1804,9 @@ static void calculate_times(struct dhcp_context *context, unsigned int *min_time *preferred_timep = preferred_time; } -static void update_leases(struct state *state, struct dhcp_context *context, struct in6_addr *addr, unsigned int lease_time, time_t now) +static struct dhcp_lease *update_leases(struct state *state, struct dhcp_context *context, struct dhcp_lease *lease, + struct in6_addr *addr, int prefix_len, unsigned int lease_time, time_t now) { - struct dhcp_lease *lease = lease6_find_by_addr(addr, 128, 0); #ifdef HAVE_SCRIPT struct dhcp_netid *tagif = run_tag_if(state->tags); #endif @@ -1759,7 +1814,7 @@ static void update_leases(struct state *state, struct dhcp_context *context, str (void)context; if (!lease) - lease = lease6_allocate(addr, state->ia_type == OPTION6_IA_NA ? LEASE_NA : LEASE_TA); + lease = lease6_allocate(addr, prefix_len, state->ia_type == OPTION6_IA_NA ? LEASE_NA : (state->ia_type == OPTION6_IA_TA ? LEASE_TA : LEASE_PD)); if (lease) { @@ -1832,7 +1887,7 @@ static void update_leases(struct state *state, struct dhcp_context *context, str inet_ntop(AF_INET6, state->link_address, daemon->addrbuff, ADDRSTRLEN); lease_add_extradata(lease, (unsigned char *)daemon->addrbuff, state->link_address ? strlen(daemon->addrbuff) : 0, 0); - + if ((class_opt = opt6_find(state->packet_options, state->end, OPTION6_USER_CLASS, 2))) { void *enc_opt, *enc_end = opt6_ptr(class_opt, opt6_len(class_opt)); @@ -1843,6 +1898,8 @@ static void update_leases(struct state *state, struct dhcp_context *context, str #endif } + + return lease; } @@ -1995,6 +2052,25 @@ static void *opt6_next(void *opts, void *end) return opts + opt_len; } +static int check_context(struct state *state) +{ + if (state->context) + return 1; + + if (state->link_address) + { + inet_ntop(AF_INET6, state->link_address, daemon->addrbuff, ADDRSTRLEN); + my_syslog(MS_DHCP | LOG_WARNING, + _("no address range available for DHCPv6 request from relay at %s"), + daemon->addrbuff); + } + else + my_syslog(MS_DHCP | LOG_WARNING, + _("no address range available for DHCPv6 request via %s"), state->iface_name); + + return 0; +} + static unsigned int opt6_uint(unsigned char *opt, int offset, int size) { /* this worries about unaligned data and byte order */ @@ -2134,4 +2210,258 @@ unsigned short relay_reply6(struct sockaddr_in6 *peer, ssize_t sz, char *arrival return 0; } + +#ifdef HAVE_PD +static int handle_pd(int msg_type, struct state *state, struct dhcp_netid *tags, void *ia_option, void *ia_end, time_t now) +{ + unsigned int min_time = 0xffffffff; + int o, o1; + int t1cntr, iacntr; + + if (state->ia_type != OPTION6_IA_PD) + return 0; + + switch (msg_type) + { + case DHCP6SOLICIT: + { + /* Go through hints, and fullfil all we can. If no prefixes found that way (or no hints) + then find exactly one prefix */ + + struct dhcp_context *context; + struct dhcp_lease *lease = lease6_find_by_client(NULL, LEASE_PD, state->clid, state->clid_len, state->iaid); + unsigned int preferred_time = 0, valid_time = 0; + + /* for (; ia_option; ia_option = opt6_find(opt6_next(ia_option, ia_end), ia_end, OPTION6_IAPREFIX, 25)) + { + int prefix_len = opt6_uint(ia_option, 8, 1); + struct in6_addr *req_addr = opt6_ptr(ia_option, 9); + } + */ + + /* Client has existing lease, offer that prefix again, else find available prefix */ + if (!lease || !(context = find_prefix_context(tags, &lease->addr6, lease->prefix_len))) + for (context = daemon->prefix_contexts; context; context = context->next) + if (match_netid(context->filter, tags, 1) && !lease6_find_by_prefix(&context->start6, context->prefix)) + break; + + /* Now context is non-NULL is we have a prefix, and lease is non-NULL is it's already leased to us. */ + o = build_ia(state, &t1cntr); + + if (context) + { + o1 = new_opt6(OPTION6_IAPREFIX); + calculate_times(context, &min_time, &valid_time, &preferred_time, context->lease_time); + put_opt6_long(preferred_time); + put_opt6_long(valid_time); + put_opt6_char(context->prefix); + put_opt6(&context->start6, IN6ADDRSZ); + end_opt6(o1); + o1 = new_opt6(OPTION6_STATUS_CODE); + put_opt6_short(DHCP6SUCCESS); + put_opt6_string(_("success")); + end_opt6(o1); + log6_quiet(state, "DHCPADVERTISE", &context->start6, ""); + } + else + { + o1 = new_opt6(OPTION6_STATUS_CODE); + put_opt6_short(DHCP6NOPREFIX); + put_opt6_string(_("no prefixes available")); + end_opt6(o1); + log6_packet(state, "DHCPADVERTISE", NULL, "no prefixes available"); + } + + end_ia(t1cntr, min_time, 0); + end_opt6(o); + + break; + } + + case DHCP6REQUEST: + /* Ensure requests are fulfilable and create leases */ + o = build_ia(state, &t1cntr); + + for (; ia_option; ia_option = opt6_find(opt6_next(ia_option, ia_end), ia_end, OPTION6_IAPREFIX, 25)) + { + int prefix_len = opt6_uint(ia_option, 8, 1); + struct in6_addr *req_addr = opt6_ptr(ia_option, 9); + unsigned int preferred_time = opt6_uint(ia_option, 0, 4); + unsigned int valid_time = opt6_uint(ia_option, 4, 4); + struct dhcp_context *context = find_prefix_context(tags, req_addr, prefix_len); + struct dhcp_lease *lease = lease6_find_by_prefix(req_addr, prefix_len); + + if (!context) + { + /* Prefix not in pool for this client*/ + o1 = new_opt6(OPTION6_STATUS_CODE); + put_opt6_short(DHCP6UNSPEC); + put_opt6_string(_("prefix unavailable")); + end_opt6(o1); + log6_packet(state, "DHCPREPLY", req_addr, "prefix unavailable"); + } + else if (lease && (lease->iaid != state->iaid || + lease->clid_len != state->clid_len || + memcmp(lease->clid, state->clid, state->clid_len) != 0)) + { + /* Prefix leased to another DUID/IAID */ + o1 = new_opt6(OPTION6_STATUS_CODE); + put_opt6_short(DHCP6UNSPEC); + put_opt6_string(_("prefix in use")); + end_opt6(o1); + log6_packet(state, "DHCPREPLY", req_addr, "prefix in use"); + } + else + { + o1 = new_opt6(OPTION6_IAPREFIX); + calculate_times(context, &min_time, &valid_time, &preferred_time, context->lease_time); + put_opt6_long(preferred_time); + put_opt6_long(valid_time); + put_opt6_char(prefix_len); + put_opt6(req_addr, sizeof(*req_addr)); + end_opt6(o1); + o1 = new_opt6(OPTION6_STATUS_CODE); + put_opt6_short(DHCP6SUCCESS); + put_opt6_string(_("success")); + end_opt6(o1); + if ((lease = update_leases(state, context, lease, req_addr, prefix_len, valid_time, now))) + lease->client_addr = state->peer_address; + log6_quiet(state, "DHCPREPLY", req_addr, ""); + } + } + + end_ia(t1cntr, min_time, 0); + end_opt6(o); + + break; + + case DHCP6RENEW: + case DHCP6REBIND: + + o = build_ia(state, &t1cntr); + iacntr = save_counter(-1); + + for (; ia_option; ia_option = opt6_find(opt6_next(ia_option, ia_end), ia_end, OPTION6_IAPREFIX, 25)) + { + struct dhcp_lease *lease = NULL; + struct in6_addr *req_addr = opt6_ptr(ia_option, 9); + unsigned int preferred_time = opt6_uint(ia_option, 0, 4); + unsigned int valid_time = opt6_uint(ia_option, 4, 4); + int prefix_len = opt6_uint(ia_option, 8, 1); + char *message = NULL; + struct dhcp_context *context = find_prefix_context(tags, req_addr, prefix_len); + + /* May have more than one lease, find the correct one. */ + while ((lease = lease6_find_by_client(lease, LEASE_PD, state->clid, state->clid_len, state->iaid))) + if (IN6_ARE_ADDR_EQUAL(&lease->addr6, req_addr) && prefix_len == lease->prefix_len) + break; + + if (!lease) + { + /* If the server cannot find a client entry for the IA the server + returns the IA containing no addresses with a Status Code option set + to NoBinding in the Reply message. */ + save_counter(iacntr); + t1cntr = 0; + + log6_packet(state, "DHCPREPLY", req_addr, _("lease not found")); + + o1 = new_opt6(OPTION6_STATUS_CODE); + put_opt6_short(DHCP6NOBINDING); + put_opt6_string(_("no binding found")); + end_opt6(o1); + + preferred_time = valid_time = 0; + break; + } + + if (context) + { + calculate_times(context, &min_time, &valid_time, &preferred_time, context->lease_time); + + lease_set_expires(lease, valid_time, now); + /* Update MAC record in case it's new information. */ + if (state->mac_len != 0) + lease_set_hwaddr(lease, state->mac, state->clid, state->mac_len, state->mac_type, state->clid_len, now, 0); + + if (preferred_time == 0) + message = _("deprecated"); + } + else + { + preferred_time = valid_time = 0; + message = _("address invalid"); + } + + if (message) + log6_packet(state, "DHCPREPLY", req_addr, message); + else + log6_quiet(state, "DHCPREPLY", req_addr, message); + + o1 = new_opt6(OPTION6_IAPREFIX); + put_opt6_long(preferred_time); + put_opt6_long(valid_time); + put_opt6_char(context->prefix); + put_opt6(req_addr, sizeof(*req_addr)); + end_opt6(o1); + } + + end_ia(t1cntr, min_time, 1); + end_opt6(o); + break; + + case DHCP6RELEASE: + { + int made_ia = 0; + + for (; ia_option; ia_option = opt6_find(opt6_next(ia_option, ia_end), ia_end, OPTION6_IAPREFIX, 25)) + { + struct dhcp_lease *lease; + + if ((lease = lease6_find(state->clid, state->clid_len, LEASE_PD, state->iaid, opt6_ptr(ia_option, 0)))) + lease_prune(lease, now); + else + { + if (!made_ia) + { + o = new_opt6(state->ia_type); + put_opt6_long(state->iaid); + put_opt6_long(0); + put_opt6_long(0); + made_ia = 1; + } + + o1 = new_opt6(OPTION6_IAPREFIX); + put_opt6_long(0); + put_opt6_long(0); + /* copy prefix-len and address */ + put_opt6(opt6_ptr(ia_option, 8), IN6ADDRSZ+1); + end_opt6(o1); + } + } + + if (made_ia) + { + o1 = new_opt6(OPTION6_STATUS_CODE); + put_opt6_short(DHCP6NOBINDING); + put_opt6_string(_("no binding found")); + end_opt6(o1); + + end_opt6(o); + } + } + + o1 = new_opt6(OPTION6_STATUS_CODE); + put_opt6_short(DHCP6SUCCESS); + put_opt6_string(_("release received")); + end_opt6(o1); + + break; + } + + return 1; + +} +#endif + #endif -- cgit v1.2.1