summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSimon Kelley <simon@thekelleys.org.uk>2014-05-01 16:37:43 +0100
committerSimon Kelley <simon@thekelleys.org.uk>2014-05-01 16:37:43 +0100
commit1e7f82f4d8d86ee3d5412ae15e61c1cdad621b4c (patch)
tree24ba094e6d755d3d27696e15d4c36dfc4050d7da
parent4872aa747b24238c0859166eaae0ae3d89364244 (diff)
downloaddnsmasq-pd.tar.gz
First commit for DHCPv6_PD.pd
-rw-r--r--man/dnsmasq.814
-rw-r--r--src/config.h9
-rw-r--r--src/dhcp-common.c8
-rw-r--r--src/dhcp6-protocol.h3
-rw-r--r--src/dhcp6.c15
-rw-r--r--src/dnsmasq.c29
-rw-r--r--src/dnsmasq.h22
-rw-r--r--src/helper.c82
-rw-r--r--src/lease.c99
-rw-r--r--src/option.c21
-rw-r--r--src/rfc3315.c408
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>[,tag:<tag>],]<IPv6addr>,<prefix-len>[,<lease time>]
+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=[<hwaddr>][,id:<client_id>|*][,set:<tag>][,<ipaddr>][,<hostname>][,<lease_time>][,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, "<ipaddr>,...", gettext_noop("Enable DHCP in the range given with lease duration."), NULL },
+ { LOPT_DHCP_PREFIX, ARG_DUP, "<ipaddr>,...", gettext_noop("Enable DHCP-PD for given prefix with lease duration."), NULL },
{ 'g', ARG_ONE, "<groupname>", gettext_noop("Change to this group after startup (defaults to %s)."), CHGRP },
{ 'G', ARG_DUP, "<hostspec>", gettext_noop("Set address or hostname for a specified machine."), NULL },
{ LOPT_DHCP_HOST, ARG_DUP, "<path>", 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, "<prefix>");
+ }
+ 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, "<prefix>");
+ }
+ }
+
+ 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