2013-04-05 04:31:04 +08:00
|
|
|
/*
|
2012-10-12 18:31:51 +08:00
|
|
|
* dhcpcd - DHCP client daemon
|
2014-01-14 20:23:36 +08:00
|
|
|
* Copyright (c) 2006-2014 Roy Marples <roy@marples.name>
|
2012-10-12 18:31:51 +08:00
|
|
|
* All rights reserved
|
|
|
|
|
|
|
|
* Redistribution and use in source and binary forms, with or without
|
|
|
|
* modification, are permitted provided that the following conditions
|
|
|
|
* are met:
|
|
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
|
|
* notice, this list of conditions and the following disclaimer.
|
|
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
|
|
* documentation and/or other materials provided with the distribution.
|
|
|
|
*
|
|
|
|
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
|
|
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
|
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
|
|
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
|
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
|
|
|
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
|
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
|
|
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
|
|
|
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
|
|
|
* SUCH DAMAGE.
|
|
|
|
*/
|
|
|
|
|
2013-05-03 20:11:49 +08:00
|
|
|
/* TODO: We should decline dupliate addresses detected */
|
|
|
|
|
2012-11-07 07:40:15 +08:00
|
|
|
#include <sys/stat.h>
|
2012-10-12 18:31:51 +08:00
|
|
|
#include <sys/utsname.h>
|
|
|
|
|
|
|
|
#include <netinet/in.h>
|
|
|
|
#ifdef __linux__
|
|
|
|
# define _LINUX_IN6_H
|
|
|
|
# include <linux/ipv6.h>
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#include <ctype.h>
|
|
|
|
#include <errno.h>
|
|
|
|
#include <fcntl.h>
|
2013-05-31 22:24:46 +08:00
|
|
|
#include <inttypes.h>
|
2012-10-12 18:31:51 +08:00
|
|
|
#include <stdlib.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <syslog.h>
|
|
|
|
#include <unistd.h>
|
|
|
|
|
2013-05-16 22:42:18 +08:00
|
|
|
#define ELOOP_QUEUE 3
|
2012-10-12 18:31:51 +08:00
|
|
|
|
|
|
|
#include "config.h"
|
|
|
|
#include "common.h"
|
|
|
|
#include "dhcp.h"
|
|
|
|
#include "dhcp6.h"
|
|
|
|
#include "duid.h"
|
|
|
|
#include "eloop.h"
|
2013-08-20 18:29:43 +08:00
|
|
|
#include "ipv6nd.h"
|
2012-10-12 18:31:51 +08:00
|
|
|
#include "platform.h"
|
2013-02-02 22:05:55 +08:00
|
|
|
#include "script.h"
|
2012-10-12 18:31:51 +08:00
|
|
|
|
|
|
|
#ifndef __UNCONST
|
|
|
|
#define __UNCONST(a) ((void *)(unsigned long)(const void *)(a))
|
|
|
|
#endif
|
|
|
|
|
2012-10-31 18:37:13 +08:00
|
|
|
/* DHCPCD Project has been assigned an IANA PEN of 40712 */
|
|
|
|
#define DHCPCD_IANA_PEN 40712
|
2012-10-12 18:31:51 +08:00
|
|
|
|
|
|
|
/* Unsure if I want this */
|
|
|
|
//#define VENDOR_SPLIT
|
|
|
|
|
|
|
|
struct dhcp6_op {
|
|
|
|
uint16_t type;
|
|
|
|
const char *name;
|
|
|
|
};
|
|
|
|
|
|
|
|
static const struct dhcp6_op dhcp6_ops[] = {
|
|
|
|
{ DHCP6_SOLICIT, "SOLICIT6" },
|
2012-11-07 07:40:15 +08:00
|
|
|
{ DHCP6_ADVERTISE, "ADVERTISE6" },
|
2012-10-12 18:31:51 +08:00
|
|
|
{ DHCP6_REQUEST, "REQUEST6" },
|
|
|
|
{ DHCP6_REPLY, "REPLY6" },
|
2012-11-07 07:40:15 +08:00
|
|
|
{ DHCP6_RENEW, "RENEW6" },
|
2013-04-01 20:15:47 +08:00
|
|
|
{ DHCP6_REBIND, "REBIND6" },
|
2012-11-07 07:40:15 +08:00
|
|
|
{ DHCP6_CONFIRM, "CONFIRM6" },
|
2012-10-12 18:31:51 +08:00
|
|
|
{ DHCP6_INFORMATION_REQ, "INFORM6" },
|
2013-04-05 05:59:02 +08:00
|
|
|
{ DHCP6_RELEASE, "RELEASE6" },
|
2012-10-12 18:31:51 +08:00
|
|
|
{ 0, NULL }
|
|
|
|
};
|
|
|
|
|
2013-06-06 04:13:14 +08:00
|
|
|
struct dhcp_compat {
|
|
|
|
uint8_t dhcp_opt;
|
|
|
|
uint16_t dhcp6_opt;
|
|
|
|
};
|
|
|
|
|
|
|
|
const struct dhcp_compat dhcp_compats[] = {
|
|
|
|
{ DHO_DNSSERVER, D6_OPTION_DNS_SERVERS },
|
|
|
|
{ DHO_HOSTNAME, D6_OPTION_FQDN },
|
|
|
|
{ DHO_DNSDOMAIN, D6_OPTION_FQDN },
|
|
|
|
{ DHO_NISSERVER, D6_OPTION_NIS_SERVERS },
|
2013-06-06 04:48:09 +08:00
|
|
|
{ DHO_NTPSERVER, D6_OPTION_SNTP_SERVERS },
|
2013-06-06 07:57:03 +08:00
|
|
|
{ DHO_RAPIDCOMMIT, D6_OPTION_RAPID_COMMIT },
|
2013-06-06 04:13:14 +08:00
|
|
|
{ DHO_FQDN, D6_OPTION_FQDN },
|
2013-12-07 01:47:53 +08:00
|
|
|
{ DHO_VIVCO, D6_OPTION_VENDOR_CLASS },
|
|
|
|
{ DHO_VIVSO, D6_OPTION_VENDOR_OPTS },
|
2013-06-06 04:13:14 +08:00
|
|
|
{ DHO_DNSSEARCH, D6_OPTION_DOMAIN_LIST },
|
|
|
|
{ 0, 0 }
|
|
|
|
};
|
|
|
|
|
2014-01-30 02:33:43 +08:00
|
|
|
static const char * const dhcp6_statuses[] = {
|
|
|
|
"Success",
|
|
|
|
"Unspecified Failure",
|
|
|
|
"No Addresses Available",
|
|
|
|
"No Binding",
|
|
|
|
"Not On Link",
|
|
|
|
"Use Multicast"
|
|
|
|
};
|
|
|
|
|
2012-10-12 18:31:51 +08:00
|
|
|
void
|
2014-02-12 08:39:46 +08:00
|
|
|
dhcp6_printoptions(const struct dhcpcd_ctx *ctx)
|
2012-10-12 18:31:51 +08:00
|
|
|
{
|
2013-12-04 07:10:21 +08:00
|
|
|
size_t i;
|
2012-10-12 18:31:51 +08:00
|
|
|
const struct dhcp_opt *opt;
|
|
|
|
|
2014-02-12 08:39:46 +08:00
|
|
|
for (i = 0, opt = ctx->dhcp6_opts;
|
|
|
|
i < ctx->dhcp6_opts_len; i++, opt++)
|
2013-12-07 01:47:52 +08:00
|
|
|
printf("%05d %s\n", opt->option, opt->var);
|
2012-10-12 18:31:51 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static size_t
|
2013-12-07 01:47:53 +08:00
|
|
|
dhcp6_makevendor(struct dhcp6_option *o, const struct interface *ifp)
|
2012-10-12 18:31:51 +08:00
|
|
|
{
|
2013-12-07 01:47:53 +08:00
|
|
|
const struct if_options *ifo;
|
2012-10-12 18:31:51 +08:00
|
|
|
size_t len;
|
|
|
|
uint8_t *p;
|
|
|
|
uint16_t u16;
|
|
|
|
uint32_t u32;
|
2013-12-07 01:47:53 +08:00
|
|
|
size_t vlen, i;
|
|
|
|
const struct vivco *vivco;
|
2014-02-08 08:29:02 +08:00
|
|
|
char vendor[VENDORCLASSID_MAX_LEN];
|
2012-10-12 18:31:51 +08:00
|
|
|
|
2013-12-07 01:47:53 +08:00
|
|
|
ifo = ifp->options;
|
2012-10-12 18:31:51 +08:00
|
|
|
len = sizeof(uint32_t); /* IANA PEN */
|
2013-12-07 01:47:53 +08:00
|
|
|
if (ifo->vivco_en) {
|
|
|
|
for (i = 0, vivco = ifo->vivco;
|
|
|
|
i < ifo->vivco_len;
|
|
|
|
i++, vivco++)
|
|
|
|
len += sizeof(uint16_t) + vivco->len;
|
|
|
|
vlen = 0; /* silence bogus gcc warning */
|
|
|
|
} else {
|
2014-02-08 08:29:02 +08:00
|
|
|
vlen = dhcp_vendor(vendor, sizeof(vendor));
|
2013-12-07 01:47:53 +08:00
|
|
|
len += sizeof(uint16_t) + vlen;
|
|
|
|
}
|
2012-10-12 18:31:51 +08:00
|
|
|
|
2013-12-07 01:47:53 +08:00
|
|
|
if (len > UINT16_MAX) {
|
|
|
|
syslog(LOG_ERR, "%s: DHCPv6 Vendor Class too big", ifp->name);
|
|
|
|
return 0;
|
|
|
|
}
|
2012-10-12 18:31:51 +08:00
|
|
|
|
|
|
|
if (o) {
|
2013-12-07 01:47:53 +08:00
|
|
|
o->code = htons(D6_OPTION_VENDOR_CLASS);
|
2012-10-12 18:31:51 +08:00
|
|
|
o->len = htons(len);
|
|
|
|
p = D6_OPTION_DATA(o);
|
2013-12-07 01:47:53 +08:00
|
|
|
u32 = htonl(ifo->vivco_en ? ifo->vivco_en : DHCPCD_IANA_PEN);
|
2012-10-12 18:31:51 +08:00
|
|
|
memcpy(p, &u32, sizeof(u32));
|
|
|
|
p += sizeof(u32);
|
2013-12-07 01:47:53 +08:00
|
|
|
if (ifo->vivco_en) {
|
|
|
|
for (i = 0, vivco = ifo->vivco;
|
|
|
|
i < ifo->vivco_len;
|
|
|
|
i++, vivco++)
|
|
|
|
{
|
|
|
|
u16 = htons(vivco->len);
|
|
|
|
memcpy(p, &u16, sizeof(u16));
|
|
|
|
p += sizeof(u16);
|
|
|
|
memcpy(p, vivco->data, vivco->len);
|
|
|
|
p += vivco->len;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
u16 = htons(vlen);
|
2012-10-12 18:31:51 +08:00
|
|
|
memcpy(p, &u16, sizeof(u16));
|
|
|
|
p += sizeof(u16);
|
2013-12-07 01:47:53 +08:00
|
|
|
memcpy(p, vendor, vlen);
|
2012-10-12 18:31:51 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return len;
|
|
|
|
}
|
|
|
|
|
|
|
|
static const struct dhcp6_option *
|
2012-11-07 07:40:15 +08:00
|
|
|
dhcp6_findoption(int code, const uint8_t *d, ssize_t len)
|
2012-10-12 18:31:51 +08:00
|
|
|
{
|
|
|
|
const struct dhcp6_option *o;
|
|
|
|
|
|
|
|
code = htons(code);
|
2012-11-07 07:40:15 +08:00
|
|
|
for (o = (const struct dhcp6_option *)d;
|
2012-10-12 18:31:51 +08:00
|
|
|
len > (ssize_t)sizeof(*o);
|
|
|
|
o = D6_CNEXT_OPTION(o))
|
|
|
|
{
|
|
|
|
len -= sizeof(*o) + ntohs(o->len);
|
|
|
|
if (len < 0) {
|
|
|
|
errno = EINVAL;
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
if (o->code == code)
|
|
|
|
return o;
|
|
|
|
}
|
|
|
|
|
|
|
|
errno = ESRCH;
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2013-11-28 04:21:17 +08:00
|
|
|
static const uint8_t *
|
2014-02-12 08:39:46 +08:00
|
|
|
dhcp6_getoption(struct dhcpcd_ctx *ctx,
|
|
|
|
unsigned int *os, unsigned int *code, unsigned int *len,
|
2013-12-07 01:47:53 +08:00
|
|
|
const uint8_t *od, unsigned int ol, struct dhcp_opt **oopt)
|
2013-11-28 04:21:17 +08:00
|
|
|
{
|
|
|
|
const struct dhcp6_option *o;
|
2013-12-04 01:55:40 +08:00
|
|
|
size_t i;
|
|
|
|
struct dhcp_opt *opt;
|
2013-11-28 04:21:17 +08:00
|
|
|
|
2013-12-04 01:55:40 +08:00
|
|
|
if (od) {
|
|
|
|
*os = sizeof(*o);
|
|
|
|
if (ol < *os) {
|
|
|
|
errno = EINVAL;
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
o = (const struct dhcp6_option *)od;
|
|
|
|
*len = ntohs(o->len);
|
2013-12-07 01:47:53 +08:00
|
|
|
if (*len > ol) {
|
2013-12-04 01:55:40 +08:00
|
|
|
errno = EINVAL;
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
*code = ntohs(o->code);
|
|
|
|
} else
|
|
|
|
o = NULL;
|
|
|
|
|
2014-02-12 08:39:46 +08:00
|
|
|
for (i = 0, opt = ctx->dhcp6_opts;
|
|
|
|
i < ctx->dhcp6_opts_len; i++, opt++)
|
|
|
|
{
|
2013-12-04 01:55:40 +08:00
|
|
|
if (opt->option == *code) {
|
|
|
|
*oopt = opt;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (o)
|
|
|
|
return D6_COPTION_DATA(o);
|
|
|
|
return NULL;
|
2013-11-28 04:21:17 +08:00
|
|
|
}
|
|
|
|
|
2012-11-07 07:40:15 +08:00
|
|
|
static const struct dhcp6_option *
|
2013-11-28 04:21:17 +08:00
|
|
|
dhcp6_getmoption(int code, const struct dhcp6_message *m, ssize_t len)
|
2012-11-07 07:40:15 +08:00
|
|
|
{
|
|
|
|
|
|
|
|
len -= sizeof(*m);
|
|
|
|
return dhcp6_findoption(code,
|
|
|
|
(const uint8_t *)D6_CFIRST_OPTION(m), len);
|
|
|
|
}
|
2012-10-12 18:31:51 +08:00
|
|
|
|
|
|
|
static int
|
|
|
|
dhcp6_updateelapsed(struct interface *ifp, struct dhcp6_message *m, ssize_t len)
|
|
|
|
{
|
|
|
|
struct dhcp6_state *state;
|
|
|
|
const struct dhcp6_option *co;
|
|
|
|
struct dhcp6_option *o;
|
|
|
|
time_t up;
|
|
|
|
uint16_t u16;
|
|
|
|
|
2013-11-28 04:21:17 +08:00
|
|
|
co = dhcp6_getmoption(D6_OPTION_ELAPSED, m, len);
|
2012-10-12 18:31:51 +08:00
|
|
|
if (co == NULL)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
o = __UNCONST(co);
|
|
|
|
state = D6_STATE(ifp);
|
|
|
|
up = uptime() - state->start_uptime;
|
|
|
|
if (up < 0 || up > (time_t)UINT16_MAX)
|
|
|
|
up = (time_t)UINT16_MAX;
|
|
|
|
u16 = htons(up);
|
|
|
|
memcpy(D6_OPTION_DATA(o), &u16, sizeof(u16));
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2012-11-07 07:40:15 +08:00
|
|
|
static void
|
2013-03-28 14:38:27 +08:00
|
|
|
dhcp6_newxid(const struct interface *ifp, struct dhcp6_message *m)
|
2012-11-07 07:40:15 +08:00
|
|
|
{
|
|
|
|
uint32_t xid;
|
|
|
|
|
2013-03-28 14:38:27 +08:00
|
|
|
if (ifp->options->options & DHCPCD_XID_HWADDR &&
|
2013-04-01 20:15:47 +08:00
|
|
|
ifp->hwlen >= sizeof(xid))
|
2013-03-28 14:38:27 +08:00
|
|
|
/* The lower bits are probably more unique on the network */
|
|
|
|
memcpy(&xid, (ifp->hwaddr + ifp->hwlen) - sizeof(xid),
|
|
|
|
sizeof(xid));
|
|
|
|
else
|
|
|
|
xid = arc4random();
|
|
|
|
|
2012-11-07 07:40:15 +08:00
|
|
|
m->xid[0] = (xid >> 16) & 0xff;
|
|
|
|
m->xid[1] = (xid >> 8) & 0xff;
|
|
|
|
m->xid[2] = xid & 0xff;
|
|
|
|
}
|
|
|
|
|
2012-10-12 18:31:51 +08:00
|
|
|
static int
|
|
|
|
dhcp6_makemessage(struct interface *ifp)
|
|
|
|
{
|
|
|
|
struct dhcp6_state *state;
|
2012-11-07 07:40:15 +08:00
|
|
|
struct dhcp6_message *m;
|
|
|
|
struct dhcp6_option *o, *so;
|
2013-06-05 18:37:45 +08:00
|
|
|
const struct dhcp6_option *si, *unicast;
|
2012-11-07 07:40:15 +08:00
|
|
|
ssize_t len, ml;
|
2013-04-01 20:15:47 +08:00
|
|
|
size_t l;
|
2014-01-25 09:35:53 +08:00
|
|
|
int auth_len;
|
2013-04-01 20:15:47 +08:00
|
|
|
uint8_t u8;
|
2014-01-25 09:35:53 +08:00
|
|
|
uint16_t *u16, n_options, type;
|
2014-02-12 08:39:46 +08:00
|
|
|
struct if_options *ifo;
|
2012-10-12 18:31:51 +08:00
|
|
|
const struct dhcp_opt *opt;
|
2013-04-01 20:15:47 +08:00
|
|
|
uint8_t IA, *p;
|
2012-11-07 07:40:15 +08:00
|
|
|
uint32_t u32;
|
|
|
|
const struct ipv6_addr *ap;
|
2014-02-12 08:39:46 +08:00
|
|
|
char hbuf[HOSTNAME_MAX_LEN + 1];
|
|
|
|
const char *hostname;
|
2013-07-23 03:05:54 +08:00
|
|
|
int fqdn;
|
2012-10-12 18:31:51 +08:00
|
|
|
|
|
|
|
state = D6_STATE(ifp);
|
|
|
|
if (state->send) {
|
|
|
|
free(state->send);
|
|
|
|
state->send = NULL;
|
|
|
|
}
|
|
|
|
|
2013-02-03 18:35:59 +08:00
|
|
|
ifo = ifp->options;
|
2013-07-23 03:05:54 +08:00
|
|
|
fqdn = ifo->fqdn;
|
|
|
|
|
2013-12-10 21:15:19 +08:00
|
|
|
if (fqdn == FQDN_DISABLE && ifo->options & DHCPCD_HOSTNAME) {
|
2013-07-23 03:05:54 +08:00
|
|
|
/* We're sending the DHCPv4 hostname option, so send FQDN as
|
|
|
|
* DHCPv6 has no FQDN option and DHCPv4 must not send
|
|
|
|
* hostname and FQDN according to RFC4702 */
|
2013-12-10 21:15:19 +08:00
|
|
|
fqdn = FQDN_BOTH;
|
|
|
|
}
|
|
|
|
if (fqdn != FQDN_DISABLE) {
|
2013-07-23 03:05:54 +08:00
|
|
|
if (ifo->hostname[0] == '\0')
|
2014-02-12 08:39:46 +08:00
|
|
|
hostname = get_hostname(hbuf, sizeof(hbuf),
|
|
|
|
ifo->options & DHCPCD_HOSTNAME_SHORT ? 1 : 0);
|
2013-07-23 03:05:54 +08:00
|
|
|
else
|
|
|
|
hostname = ifo->hostname;
|
2014-02-12 08:39:46 +08:00
|
|
|
} else
|
|
|
|
hostname = NULL; /* appearse gcc */
|
2013-07-23 03:05:54 +08:00
|
|
|
|
|
|
|
/* Work out option size first */
|
2013-06-06 04:13:14 +08:00
|
|
|
n_options = 0;
|
2012-10-12 18:31:51 +08:00
|
|
|
len = 0;
|
2012-11-07 07:40:15 +08:00
|
|
|
si = NULL;
|
2013-04-05 05:59:02 +08:00
|
|
|
if (state->state != DH6S_RELEASE) {
|
2014-02-12 08:39:46 +08:00
|
|
|
for (l = 0, opt = ifp->ctx->dhcp6_opts;
|
|
|
|
l < ifp->ctx->dhcp6_opts_len;
|
|
|
|
l++, opt++)
|
|
|
|
{
|
2013-06-06 07:57:03 +08:00
|
|
|
if (!(opt->type & NOREQ) &&
|
|
|
|
(opt->type & REQUEST ||
|
|
|
|
has_option_mask(ifo->requestmask6, opt->option)))
|
2013-06-06 04:13:14 +08:00
|
|
|
{
|
|
|
|
n_options++;
|
2013-05-03 21:38:54 +08:00
|
|
|
len += sizeof(*u16);
|
2013-06-06 04:13:14 +08:00
|
|
|
}
|
2013-04-05 05:59:02 +08:00
|
|
|
}
|
2013-06-06 04:13:14 +08:00
|
|
|
if (len)
|
|
|
|
len += sizeof(*o);
|
2013-06-06 00:23:24 +08:00
|
|
|
|
2013-07-23 03:05:54 +08:00
|
|
|
if (fqdn != FQDN_DISABLE)
|
2013-06-06 00:23:24 +08:00
|
|
|
len += sizeof(*o) + 1 + encode_rfc1035(hostname, NULL);
|
2014-01-31 22:25:18 +08:00
|
|
|
|
2014-02-01 02:33:11 +08:00
|
|
|
if ((ifo->auth.options & DHCPCD_AUTH_SENDREQUIRE) !=
|
|
|
|
DHCPCD_AUTH_SENDREQUIRE)
|
|
|
|
len += sizeof(*o); /* Reconfigure Accept */
|
2012-10-12 18:31:51 +08:00
|
|
|
}
|
|
|
|
|
2012-11-07 07:40:15 +08:00
|
|
|
len += sizeof(*state->send);
|
2014-02-12 08:39:46 +08:00
|
|
|
len += sizeof(*o) + ifp->ctx->duid_len;
|
2012-10-12 18:31:51 +08:00
|
|
|
len += sizeof(*o) + sizeof(uint16_t); /* elapsed */
|
2013-12-07 01:47:53 +08:00
|
|
|
len += sizeof(*o) + dhcp6_makevendor(NULL, ifp);
|
2013-04-01 20:15:47 +08:00
|
|
|
|
|
|
|
/* IA */
|
2012-11-07 07:40:15 +08:00
|
|
|
m = NULL;
|
|
|
|
ml = 0;
|
|
|
|
switch(state->state) {
|
|
|
|
case DH6S_REQUEST:
|
|
|
|
m = state->recv;
|
|
|
|
ml = state->recv_len;
|
|
|
|
/* FALLTHROUGH */
|
2013-04-05 05:59:02 +08:00
|
|
|
case DH6S_RELEASE:
|
|
|
|
/* FALLTHROUGH */
|
2012-11-07 07:40:15 +08:00
|
|
|
case DH6S_RENEW:
|
|
|
|
if (m == NULL) {
|
|
|
|
m = state->new;
|
|
|
|
ml = state->new_len;
|
|
|
|
}
|
2013-11-28 04:21:17 +08:00
|
|
|
si = dhcp6_getmoption(D6_OPTION_SERVERID, m, ml);
|
2014-01-14 18:56:21 +08:00
|
|
|
if (si == NULL) {
|
|
|
|
errno = ESRCH;
|
|
|
|
return -1;
|
|
|
|
}
|
2012-11-07 07:40:15 +08:00
|
|
|
len += sizeof(*si) + ntohs(si->len);
|
|
|
|
/* FALLTHROUGH */
|
2013-04-01 20:15:47 +08:00
|
|
|
case DH6S_REBIND:
|
|
|
|
/* FALLTHROUGH */
|
|
|
|
case DH6S_CONFIRM:
|
2012-11-07 07:40:15 +08:00
|
|
|
if (m == NULL) {
|
|
|
|
m = state->new;
|
|
|
|
ml = state->new_len;
|
|
|
|
}
|
|
|
|
TAILQ_FOREACH(ap, &state->addrs, next) {
|
2013-06-12 06:49:07 +08:00
|
|
|
if (ap->prefix_vltime == 0)
|
|
|
|
continue;
|
2013-04-01 20:15:47 +08:00
|
|
|
if (ifo->ia_type == D6_OPTION_IA_PD)
|
|
|
|
len += sizeof(*o) + sizeof(u8) +
|
2013-04-02 15:01:11 +08:00
|
|
|
sizeof(u32) + sizeof(u32) +
|
2013-04-01 20:15:47 +08:00
|
|
|
sizeof(ap->prefix.s6_addr);
|
|
|
|
else
|
|
|
|
len += sizeof(*o) + sizeof(ap->addr.s6_addr) +
|
|
|
|
sizeof(u32) + sizeof(u32);
|
2012-11-07 07:40:15 +08:00
|
|
|
}
|
|
|
|
/* FALLTHROUGH */
|
|
|
|
case DH6S_INIT: /* FALLTHROUGH */
|
|
|
|
case DH6S_DISCOVER:
|
2013-11-15 21:43:41 +08:00
|
|
|
len += ifo->ia_len * (sizeof(*o) + (sizeof(u32) * 3));
|
2013-04-01 20:15:47 +08:00
|
|
|
IA = 1;
|
2012-11-07 07:40:15 +08:00
|
|
|
break;
|
|
|
|
default:
|
2013-04-01 20:15:47 +08:00
|
|
|
IA = 0;
|
2012-11-07 07:40:15 +08:00
|
|
|
}
|
|
|
|
|
2013-06-06 07:57:03 +08:00
|
|
|
if (state->state == DH6S_DISCOVER &&
|
2014-02-12 08:39:46 +08:00
|
|
|
!(ifp->ctx->options & DHCPCD_TEST) &&
|
2013-06-06 07:57:03 +08:00
|
|
|
has_option_mask(ifo->requestmask6, D6_OPTION_RAPID_COMMIT))
|
|
|
|
len += sizeof(*o);
|
|
|
|
|
2012-11-07 07:40:15 +08:00
|
|
|
if (m == NULL) {
|
|
|
|
m = state->new;
|
|
|
|
ml = state->new_len;
|
|
|
|
}
|
2013-06-05 18:37:45 +08:00
|
|
|
unicast = NULL;
|
|
|
|
/* Depending on state, get the unicast address */
|
2012-11-07 07:40:15 +08:00
|
|
|
switch(state->state) {
|
|
|
|
break;
|
|
|
|
case DH6S_INIT: /* FALLTHROUGH */
|
|
|
|
case DH6S_DISCOVER:
|
2014-01-25 09:35:53 +08:00
|
|
|
type = DHCP6_SOLICIT;
|
2012-11-07 07:40:15 +08:00
|
|
|
break;
|
2013-04-01 20:15:47 +08:00
|
|
|
case DH6S_REQUEST:
|
2014-01-25 09:35:53 +08:00
|
|
|
type = DHCP6_REQUEST;
|
2013-11-28 04:21:17 +08:00
|
|
|
unicast = dhcp6_getmoption(D6_OPTION_UNICAST, m, ml);
|
2012-11-07 07:40:15 +08:00
|
|
|
break;
|
2013-04-01 20:15:47 +08:00
|
|
|
case DH6S_CONFIRM:
|
2014-01-25 09:35:53 +08:00
|
|
|
type = DHCP6_CONFIRM;
|
2013-04-01 20:15:47 +08:00
|
|
|
break;
|
|
|
|
case DH6S_REBIND:
|
2014-01-25 09:35:53 +08:00
|
|
|
type = DHCP6_REBIND;
|
2013-04-01 20:15:47 +08:00
|
|
|
break;
|
2012-11-07 07:40:15 +08:00
|
|
|
case DH6S_RENEW:
|
2014-01-25 09:35:53 +08:00
|
|
|
type = DHCP6_RENEW;
|
2013-11-28 04:21:17 +08:00
|
|
|
unicast = dhcp6_getmoption(D6_OPTION_UNICAST, m, ml);
|
2012-11-07 07:40:15 +08:00
|
|
|
break;
|
|
|
|
case DH6S_INFORM:
|
2014-01-25 09:35:53 +08:00
|
|
|
type = DHCP6_INFORMATION_REQ;
|
2012-11-07 07:40:15 +08:00
|
|
|
break;
|
2013-04-05 05:59:02 +08:00
|
|
|
case DH6S_RELEASE:
|
2014-01-25 09:35:53 +08:00
|
|
|
type = DHCP6_RELEASE;
|
2013-11-28 04:21:17 +08:00
|
|
|
unicast = dhcp6_getmoption(D6_OPTION_UNICAST, m, ml);
|
2013-04-05 05:59:02 +08:00
|
|
|
break;
|
2012-11-07 07:40:15 +08:00
|
|
|
default:
|
|
|
|
errno = EINVAL;
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2014-01-25 09:35:53 +08:00
|
|
|
if (ifo->auth.options & DHCPCD_AUTH_SEND) {
|
|
|
|
auth_len = dhcp_auth_encode(&ifo->auth, state->auth.token,
|
|
|
|
NULL, 0, 6, type, NULL, 0);
|
|
|
|
if (auth_len > 0)
|
|
|
|
len += sizeof(*o) + auth_len;
|
|
|
|
} else
|
|
|
|
auth_len = 0; /* appease GCC */
|
|
|
|
|
|
|
|
state->send = malloc(len);
|
|
|
|
if (state->send == NULL)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
state->send_len = len;
|
|
|
|
state->send->type = type;
|
|
|
|
|
2013-06-05 18:37:45 +08:00
|
|
|
/* If we found a unicast option, copy it to our state for sending */
|
|
|
|
if (unicast && ntohs(unicast->len) == sizeof(state->unicast.s6_addr))
|
|
|
|
memcpy(&state->unicast.s6_addr, D6_COPTION_DATA(unicast),
|
|
|
|
sizeof(state->unicast.s6_addr));
|
|
|
|
else
|
|
|
|
state->unicast = in6addr_any;
|
|
|
|
|
2013-03-28 14:38:27 +08:00
|
|
|
dhcp6_newxid(ifp, state->send);
|
2012-10-12 18:31:51 +08:00
|
|
|
|
|
|
|
o = D6_FIRST_OPTION(state->send);
|
|
|
|
o->code = htons(D6_OPTION_CLIENTID);
|
2014-02-12 08:39:46 +08:00
|
|
|
o->len = htons(ifp->ctx->duid_len);
|
|
|
|
memcpy(D6_OPTION_DATA(o), ifp->ctx->duid, ifp->ctx->duid_len);
|
2012-10-12 18:31:51 +08:00
|
|
|
|
2012-11-07 07:40:15 +08:00
|
|
|
if (si) {
|
|
|
|
o = D6_NEXT_OPTION(o);
|
|
|
|
memcpy(o, si, sizeof(*si) + ntohs(si->len));
|
|
|
|
}
|
|
|
|
|
2012-10-12 18:31:51 +08:00
|
|
|
o = D6_NEXT_OPTION(o);
|
|
|
|
o->code = htons(D6_OPTION_ELAPSED);
|
|
|
|
o->len = htons(sizeof(uint16_t));
|
2012-11-07 07:40:15 +08:00
|
|
|
p = D6_OPTION_DATA(o);
|
|
|
|
memset(p, 0, sizeof(u16));
|
2012-10-12 18:31:51 +08:00
|
|
|
|
|
|
|
o = D6_NEXT_OPTION(o);
|
2013-12-07 01:47:53 +08:00
|
|
|
dhcp6_makevendor(o, ifp);
|
2012-10-12 18:31:51 +08:00
|
|
|
|
2013-06-06 07:57:03 +08:00
|
|
|
if (state->state == DH6S_DISCOVER &&
|
2014-02-12 08:39:46 +08:00
|
|
|
!(ifp->ctx->options & DHCPCD_TEST) &&
|
2013-06-06 07:57:03 +08:00
|
|
|
has_option_mask(ifo->requestmask6, D6_OPTION_RAPID_COMMIT))
|
|
|
|
{
|
|
|
|
o = D6_NEXT_OPTION(o);
|
|
|
|
o->code = htons(D6_OPTION_RAPID_COMMIT);
|
|
|
|
o->len = 0;
|
|
|
|
}
|
|
|
|
|
2013-11-15 21:43:41 +08:00
|
|
|
for (l = 0; IA && l < ifo->ia_len; l++) {
|
2012-11-07 07:40:15 +08:00
|
|
|
o = D6_NEXT_OPTION(o);
|
2013-04-01 20:15:47 +08:00
|
|
|
o->code = htons(ifo->ia_type);
|
2012-11-07 07:40:15 +08:00
|
|
|
o->len = htons(sizeof(u32) + sizeof(u32) + sizeof(u32));
|
|
|
|
p = D6_OPTION_DATA(o);
|
2013-11-15 21:43:41 +08:00
|
|
|
memcpy(p, ifo->ia[l].iaid, sizeof(u32));
|
2012-11-07 07:40:15 +08:00
|
|
|
p += sizeof(u32);
|
|
|
|
memset(p, 0, sizeof(u32) + sizeof(u32));
|
|
|
|
TAILQ_FOREACH(ap, &state->addrs, next) {
|
2013-06-12 06:49:07 +08:00
|
|
|
if (ap->prefix_vltime == 0)
|
|
|
|
continue;
|
2013-11-15 21:43:41 +08:00
|
|
|
if (memcmp(ifo->ia[l].iaid, ap->iaid, sizeof(u32)))
|
2013-04-01 20:15:47 +08:00
|
|
|
continue;
|
2012-11-07 07:40:15 +08:00
|
|
|
so = D6_NEXT_OPTION(o);
|
2013-04-01 20:15:47 +08:00
|
|
|
if (ifo->ia_type == D6_OPTION_IA_PD) {
|
|
|
|
so->code = htons(D6_OPTION_IAPREFIX);
|
|
|
|
so->len = htons(sizeof(ap->prefix.s6_addr) +
|
|
|
|
sizeof(u32) + sizeof(u32) + sizeof(u8));
|
|
|
|
p = D6_OPTION_DATA(so);
|
|
|
|
u32 = htonl(ap->prefix_pltime);
|
|
|
|
memcpy(p, &u32, sizeof(u32));
|
|
|
|
p += sizeof(u32);
|
|
|
|
u32 = htonl(ap->prefix_vltime);
|
|
|
|
memcpy(p, &u32, sizeof(u32));
|
|
|
|
p += sizeof(u32);
|
|
|
|
u8 = ap->prefix_len;
|
|
|
|
memcpy(p, &u8, sizeof(u8));
|
|
|
|
p += sizeof(u8);
|
|
|
|
memcpy(p, &ap->prefix.s6_addr,
|
|
|
|
sizeof(ap->prefix.s6_addr));
|
|
|
|
/* Avoid a shadowed declaration warning by
|
|
|
|
* moving our addition outside of the htons
|
|
|
|
* macro */
|
|
|
|
u32 = ntohs(o->len) + sizeof(*so)
|
|
|
|
+ ntohs(so->len);
|
|
|
|
o->len = htons(u32);
|
|
|
|
} else {
|
|
|
|
so->code = htons(D6_OPTION_IA_ADDR);
|
|
|
|
so->len = htons(sizeof(ap->addr.s6_addr) +
|
|
|
|
sizeof(u32) + sizeof(u32));
|
|
|
|
p = D6_OPTION_DATA(so);
|
|
|
|
memcpy(p, &ap->addr.s6_addr,
|
|
|
|
sizeof(ap->addr.s6_addr));
|
|
|
|
p += sizeof(ap->addr.s6_addr);
|
|
|
|
u32 = htonl(ap->prefix_pltime);
|
|
|
|
memcpy(p, &u32, sizeof(u32));
|
|
|
|
p += sizeof(u32);
|
|
|
|
u32 = htonl(ap->prefix_vltime);
|
|
|
|
memcpy(p, &u32, sizeof(u32));
|
|
|
|
/* Avoid a shadowed declaration warning by
|
|
|
|
* moving our addition outside of the htons
|
|
|
|
* macro */
|
|
|
|
u32 = ntohs(o->len) + sizeof(*so)
|
|
|
|
+ ntohs(so->len);
|
|
|
|
o->len = htons(u32);
|
|
|
|
}
|
2012-11-07 07:40:15 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-01-31 22:25:18 +08:00
|
|
|
if (state->send->type != DHCP6_RELEASE) {
|
2013-07-23 03:05:54 +08:00
|
|
|
if (fqdn != FQDN_DISABLE) {
|
2013-06-06 00:23:24 +08:00
|
|
|
o = D6_NEXT_OPTION(o);
|
|
|
|
o->code = htons(D6_OPTION_FQDN);
|
|
|
|
p = D6_OPTION_DATA(o);
|
2013-07-23 03:05:54 +08:00
|
|
|
switch (fqdn) {
|
2013-06-06 00:23:24 +08:00
|
|
|
case FQDN_BOTH:
|
2013-06-22 17:15:35 +08:00
|
|
|
*p = D6_FQDN_BOTH;
|
2013-06-06 00:23:24 +08:00
|
|
|
break;
|
|
|
|
case FQDN_PTR:
|
2013-06-22 17:15:35 +08:00
|
|
|
*p = D6_FQDN_PTR;
|
2013-06-06 00:23:24 +08:00
|
|
|
break;
|
|
|
|
default:
|
2013-06-22 17:15:35 +08:00
|
|
|
*p = D6_FQDN_NONE;
|
2013-06-06 00:23:24 +08:00
|
|
|
break;
|
|
|
|
}
|
2013-06-22 17:15:35 +08:00
|
|
|
l = encode_rfc1035(hostname, p + 1);
|
|
|
|
if (l == 0)
|
|
|
|
*p = D6_FQDN_NONE;
|
|
|
|
o->len = htons(l + 1);
|
2013-06-06 00:23:24 +08:00
|
|
|
}
|
2013-06-06 04:13:14 +08:00
|
|
|
|
2014-02-01 02:33:11 +08:00
|
|
|
if ((ifo->auth.options & DHCPCD_AUTH_SENDREQUIRE) !=
|
|
|
|
DHCPCD_AUTH_SENDREQUIRE)
|
|
|
|
{
|
|
|
|
o = D6_NEXT_OPTION(o);
|
|
|
|
o->code = htons(D6_OPTION_RECONF_ACCEPT);
|
|
|
|
o->len = 0;
|
|
|
|
}
|
2014-01-31 22:25:18 +08:00
|
|
|
|
2013-06-06 04:13:14 +08:00
|
|
|
if (n_options) {
|
|
|
|
o = D6_NEXT_OPTION(o);
|
|
|
|
o->code = htons(D6_OPTION_ORO);
|
|
|
|
o->len = 0;
|
|
|
|
u16 = (uint16_t *)(void *)D6_OPTION_DATA(o);
|
2014-02-12 08:39:46 +08:00
|
|
|
for (l = 0, opt = ifp->ctx->dhcp6_opts;
|
|
|
|
l < ifp->ctx->dhcp6_opts_len;
|
2013-12-04 07:10:21 +08:00
|
|
|
l++, opt++)
|
|
|
|
{
|
2013-06-06 07:57:03 +08:00
|
|
|
if (!(opt->type & NOREQ) &&
|
|
|
|
(opt->type & REQUEST ||
|
2013-06-06 04:13:14 +08:00
|
|
|
has_option_mask(ifo->requestmask6,
|
2013-06-06 07:57:03 +08:00
|
|
|
opt->option)))
|
2013-06-06 04:13:14 +08:00
|
|
|
{
|
|
|
|
*u16++ = htons(opt->option);
|
|
|
|
o->len += sizeof(*u16);
|
|
|
|
}
|
2013-06-06 01:36:49 +08:00
|
|
|
}
|
2013-06-06 04:13:14 +08:00
|
|
|
o->len = htons(o->len);
|
2013-04-05 05:59:02 +08:00
|
|
|
}
|
2012-10-12 18:31:51 +08:00
|
|
|
}
|
|
|
|
|
2014-01-25 09:35:53 +08:00
|
|
|
/* This has to be the last option */
|
|
|
|
if (ifo->auth.options & DHCPCD_AUTH_SEND && auth_len > 0) {
|
|
|
|
o = D6_NEXT_OPTION(o);
|
|
|
|
o->code = htons(D6_OPTION_AUTH);
|
|
|
|
o->len = htons(auth_len);
|
2014-01-25 10:23:41 +08:00
|
|
|
/* data will be filled at send message time */
|
2014-01-25 09:35:53 +08:00
|
|
|
}
|
|
|
|
|
2012-10-12 18:31:51 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static const char *
|
|
|
|
dhcp6_get_op(uint16_t type)
|
|
|
|
{
|
|
|
|
const struct dhcp6_op *d;
|
|
|
|
|
|
|
|
for (d = dhcp6_ops; d->name; d++)
|
|
|
|
if (d->type == type)
|
|
|
|
return d->name;
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2012-11-07 07:40:15 +08:00
|
|
|
static void
|
2013-06-11 05:54:00 +08:00
|
|
|
dhcp6_freedrop_addrs(struct interface *ifp, int drop,
|
|
|
|
const struct interface *ifd)
|
2012-11-07 07:40:15 +08:00
|
|
|
{
|
|
|
|
struct dhcp6_state *state;
|
|
|
|
|
|
|
|
state = D6_STATE(ifp);
|
2013-06-12 18:58:28 +08:00
|
|
|
if (state) {
|
|
|
|
ipv6_freedrop_addrs(&state->addrs, drop, ifd);
|
|
|
|
if (drop)
|
2014-02-12 08:39:46 +08:00
|
|
|
ipv6_buildroutes(ifp->ctx);
|
2012-11-07 07:40:15 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-06-11 05:54:00 +08:00
|
|
|
static void dhcp6_delete_delegates(struct interface *ifp)
|
|
|
|
{
|
|
|
|
struct interface *ifp0;
|
|
|
|
|
2014-02-12 08:39:46 +08:00
|
|
|
if (ifp->ctx->ifaces) {
|
|
|
|
TAILQ_FOREACH(ifp0, ifp->ctx->ifaces, next) {
|
2013-06-19 16:57:23 +08:00
|
|
|
if (ifp0 != ifp)
|
|
|
|
dhcp6_freedrop_addrs(ifp0, 1, ifp);
|
|
|
|
}
|
2013-06-11 05:54:00 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-01-25 10:23:41 +08:00
|
|
|
|
|
|
|
static int
|
|
|
|
dhcp6_update_auth(struct interface *ifp, struct dhcp6_message *m, ssize_t len)
|
|
|
|
{
|
|
|
|
struct dhcp6_state *state;
|
|
|
|
const struct dhcp6_option *co;
|
|
|
|
struct dhcp6_option *o;
|
|
|
|
|
|
|
|
co = dhcp6_getmoption(D6_OPTION_AUTH, m, len);
|
|
|
|
if (co == NULL)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
o = __UNCONST(co);
|
|
|
|
state = D6_STATE(ifp);
|
|
|
|
|
|
|
|
return dhcp_auth_encode(&ifp->options->auth, state->auth.token,
|
|
|
|
(uint8_t *)state->send, state->send_len,
|
|
|
|
6, state->send->type,
|
|
|
|
D6_OPTION_DATA(o), ntohs(o->len));
|
|
|
|
}
|
|
|
|
|
2013-02-18 02:17:29 +08:00
|
|
|
static int
|
2012-10-12 18:31:51 +08:00
|
|
|
dhcp6_sendmessage(struct interface *ifp, void (*callback)(void *))
|
|
|
|
{
|
|
|
|
struct dhcp6_state *state;
|
2014-02-12 08:39:46 +08:00
|
|
|
struct ipv6_ctx *ctx;
|
2012-10-12 18:31:51 +08:00
|
|
|
struct sockaddr_in6 to;
|
|
|
|
struct cmsghdr *cm;
|
|
|
|
struct in6_pktinfo pi;
|
2012-11-07 07:40:15 +08:00
|
|
|
struct timeval RTprev;
|
|
|
|
double rnd;
|
|
|
|
suseconds_t ms;
|
|
|
|
uint8_t neg;
|
2013-06-05 18:37:45 +08:00
|
|
|
const char *broad_uni;
|
2014-02-12 08:39:46 +08:00
|
|
|
const struct in6_addr alldhcp = IN6ADDR_LINKLOCAL_ALLDHCP_INIT;
|
2013-06-05 18:37:45 +08:00
|
|
|
|
|
|
|
memset(&to, 0, sizeof(to));
|
|
|
|
to.sin6_family = AF_INET6;
|
|
|
|
to.sin6_port = htons(DHCP6_SERVER_PORT);
|
|
|
|
#ifdef SIN6_LEN
|
|
|
|
to.sin6_len = sizeof(to);
|
|
|
|
#endif
|
2012-10-12 18:31:51 +08:00
|
|
|
|
|
|
|
state = D6_STATE(ifp);
|
2013-06-05 18:37:45 +08:00
|
|
|
/* We need to ensure we have sufficient scope to unicast the address */
|
|
|
|
/* XXX FIXME: We should check any added addresses we have like from
|
|
|
|
* a Router Advertisement */
|
2013-12-13 22:54:14 +08:00
|
|
|
if (IN6_IS_ADDR_UNSPECIFIED(&state->unicast) ||
|
|
|
|
(state->state == DH6S_REQUEST &&
|
|
|
|
(!IN6_IS_ADDR_LINKLOCAL(&state->unicast) || !ipv6_linklocal(ifp))))
|
|
|
|
{
|
2014-02-12 08:39:46 +08:00
|
|
|
to.sin6_addr = alldhcp;
|
2013-06-05 18:37:45 +08:00
|
|
|
broad_uni = "broadcasting";
|
|
|
|
} else {
|
|
|
|
to.sin6_addr = state->unicast;
|
|
|
|
broad_uni = "unicasting";
|
|
|
|
}
|
|
|
|
|
2012-10-12 18:31:51 +08:00
|
|
|
if (!callback)
|
2013-06-05 18:37:45 +08:00
|
|
|
syslog(LOG_DEBUG, "%s: %s %s with xid 0x%02x%02x%02x",
|
2012-10-12 18:31:51 +08:00
|
|
|
ifp->name,
|
2013-06-05 18:37:45 +08:00
|
|
|
broad_uni,
|
2012-10-12 18:31:51 +08:00
|
|
|
dhcp6_get_op(state->send->type),
|
|
|
|
state->send->xid[0],
|
|
|
|
state->send->xid[1],
|
|
|
|
state->send->xid[2]);
|
|
|
|
else {
|
2013-06-12 23:01:59 +08:00
|
|
|
if (state->IMD) {
|
2013-06-18 17:17:28 +08:00
|
|
|
/* Some buggy PPP servers close the link too early
|
|
|
|
* after sending an invalid status in their reply
|
|
|
|
* which means this host won't see it.
|
2013-06-19 17:41:30 +08:00
|
|
|
* 1 second grace seems to be the sweet spot. */
|
2013-06-18 17:17:28 +08:00
|
|
|
if (ifp->flags & IFF_POINTOPOINT)
|
2013-06-19 17:41:30 +08:00
|
|
|
state->RT.tv_sec = 1;
|
2013-06-18 17:17:28 +08:00
|
|
|
else
|
|
|
|
state->RT.tv_sec = 0;
|
2013-06-12 23:01:59 +08:00
|
|
|
state->RT.tv_usec = arc4random() %
|
|
|
|
(state->IMD * 1000000);
|
|
|
|
timernorm(&state->RT);
|
|
|
|
broad_uni = "delaying";
|
|
|
|
goto logsend;
|
|
|
|
}
|
2012-11-07 07:40:15 +08:00
|
|
|
if (state->RTC == 0) {
|
|
|
|
RTprev.tv_sec = state->IRT;
|
|
|
|
RTprev.tv_usec = 0;
|
2013-06-12 23:01:59 +08:00
|
|
|
state->RT.tv_sec = RTprev.tv_sec;
|
2012-11-07 07:40:15 +08:00
|
|
|
state->RT.tv_usec = 0;
|
|
|
|
} else {
|
|
|
|
RTprev = state->RT;
|
|
|
|
timeradd(&state->RT, &state->RT, &state->RT);
|
2012-10-12 18:31:51 +08:00
|
|
|
}
|
2012-11-07 07:40:15 +08:00
|
|
|
|
|
|
|
rnd = DHCP6_RAND_MIN;
|
|
|
|
rnd += arc4random() % (DHCP6_RAND_MAX - DHCP6_RAND_MIN);
|
|
|
|
rnd /= 1000;
|
|
|
|
neg = (rnd < 0.0);
|
|
|
|
if (neg)
|
|
|
|
rnd = -rnd;
|
|
|
|
tv_to_ms(ms, &RTprev);
|
|
|
|
ms *= rnd;
|
|
|
|
ms_to_tv(&RTprev, ms);
|
|
|
|
if (neg)
|
|
|
|
timersub(&state->RT, &RTprev, &state->RT);
|
|
|
|
else
|
|
|
|
timeradd(&state->RT, &RTprev, &state->RT);
|
|
|
|
|
|
|
|
if (state->RT.tv_sec > state->MRT) {
|
|
|
|
RTprev.tv_sec = state->MRT;
|
|
|
|
RTprev.tv_usec = 0;
|
|
|
|
state->RT.tv_sec = state->MRT;
|
|
|
|
state->RT.tv_usec = 0;
|
|
|
|
tv_to_ms(ms, &RTprev);
|
|
|
|
ms *= rnd;
|
|
|
|
ms_to_tv(&RTprev, ms);
|
|
|
|
if (neg)
|
|
|
|
timersub(&state->RT, &RTprev, &state->RT);
|
|
|
|
else
|
|
|
|
timeradd(&state->RT, &RTprev, &state->RT);
|
|
|
|
}
|
|
|
|
|
2013-06-12 23:01:59 +08:00
|
|
|
logsend:
|
2012-10-12 18:31:51 +08:00
|
|
|
syslog(LOG_DEBUG,
|
2013-06-05 18:37:45 +08:00
|
|
|
"%s: %s %s (xid 0x%02x%02x%02x),"
|
2014-01-25 09:35:53 +08:00
|
|
|
" next in %0.1f seconds",
|
2013-06-05 18:37:45 +08:00
|
|
|
ifp->name,
|
|
|
|
broad_uni,
|
|
|
|
dhcp6_get_op(state->send->type),
|
2012-10-12 18:31:51 +08:00
|
|
|
state->send->xid[0],
|
|
|
|
state->send->xid[1],
|
|
|
|
state->send->xid[2],
|
2012-11-07 07:40:15 +08:00
|
|
|
timeval_to_double(&state->RT));
|
2013-06-12 23:01:59 +08:00
|
|
|
|
|
|
|
/* Wait the initial delay */
|
|
|
|
if (state->IMD) {
|
|
|
|
state->IMD = 0;
|
2014-02-12 08:39:46 +08:00
|
|
|
eloop_timeout_add_tv(ifp->ctx->eloop,
|
|
|
|
&state->RT, callback, ifp);
|
2013-06-12 23:01:59 +08:00
|
|
|
return 0;
|
|
|
|
}
|
2012-10-12 18:31:51 +08:00
|
|
|
}
|
|
|
|
|
2012-11-07 07:40:15 +08:00
|
|
|
/* Update the elapsed time */
|
|
|
|
dhcp6_updateelapsed(ifp, state->send, state->send_len);
|
2014-01-30 01:21:14 +08:00
|
|
|
if (ifp->options->auth.options & DHCPCD_AUTH_SEND &&
|
|
|
|
dhcp6_update_auth(ifp, state->send, state->send_len) == -1)
|
|
|
|
{
|
2014-01-25 10:23:41 +08:00
|
|
|
syslog(LOG_ERR, "%s: dhcp6_updateauth: %m", ifp->name);
|
|
|
|
return -1;
|
|
|
|
}
|
2012-11-07 07:40:15 +08:00
|
|
|
|
2014-02-12 08:39:46 +08:00
|
|
|
ctx = ifp->ctx->ipv6;
|
2013-05-17 03:02:14 +08:00
|
|
|
to.sin6_scope_id = ifp->index;
|
2014-02-12 08:39:46 +08:00
|
|
|
ctx->sndhdr.msg_name = (caddr_t)&to;
|
|
|
|
ctx->sndhdr.msg_iov[0].iov_base = (caddr_t)state->send;
|
|
|
|
ctx->sndhdr.msg_iov[0].iov_len = state->send_len;
|
2012-10-12 18:31:51 +08:00
|
|
|
|
|
|
|
/* Set the outbound interface */
|
2014-02-12 08:39:46 +08:00
|
|
|
cm = CMSG_FIRSTHDR(&ctx->sndhdr);
|
2014-02-28 04:00:39 +08:00
|
|
|
if (cm == NULL) /* unlikely */
|
|
|
|
return -1;
|
2012-10-12 18:31:51 +08:00
|
|
|
cm->cmsg_level = IPPROTO_IPV6;
|
|
|
|
cm->cmsg_type = IPV6_PKTINFO;
|
|
|
|
cm->cmsg_len = CMSG_LEN(sizeof(pi));
|
|
|
|
memset(&pi, 0, sizeof(pi));
|
|
|
|
pi.ipi6_ifindex = ifp->index;
|
|
|
|
memcpy(CMSG_DATA(cm), &pi, sizeof(pi));
|
2013-04-01 20:15:47 +08:00
|
|
|
|
2014-02-12 08:39:46 +08:00
|
|
|
if (sendmsg(ctx->dhcp_fd, &ctx->sndhdr, 0) == -1) {
|
2013-05-30 13:38:21 +08:00
|
|
|
syslog(LOG_ERR, "%s: %s: sendmsg: %m", ifp->name, __func__);
|
2013-02-18 02:17:29 +08:00
|
|
|
ifp->options->options &= ~DHCPCD_IPV6;
|
|
|
|
dhcp6_drop(ifp, "EXPIRE6");
|
|
|
|
return -1;
|
|
|
|
}
|
2012-10-12 18:31:51 +08:00
|
|
|
|
2012-11-07 07:40:15 +08:00
|
|
|
state->RTC++;
|
|
|
|
if (callback) {
|
|
|
|
if (state->MRC == 0 || state->RTC < state->MRC)
|
2014-02-12 08:39:46 +08:00
|
|
|
eloop_timeout_add_tv(ifp->ctx->eloop,
|
|
|
|
&state->RT, callback, ifp);
|
2012-11-07 07:40:15 +08:00
|
|
|
else if (state->MRC != 0 && state->MRCcallback)
|
2014-02-12 08:39:46 +08:00
|
|
|
eloop_timeout_add_tv(ifp->ctx->eloop,
|
|
|
|
&state->RT, state->MRCcallback, ifp);
|
2012-11-07 07:40:15 +08:00
|
|
|
else
|
|
|
|
syslog(LOG_WARNING, "%s: sent %d times with no reply",
|
|
|
|
ifp->name, state->RTC);
|
|
|
|
}
|
2013-02-18 02:17:29 +08:00
|
|
|
return 0;
|
2012-10-12 18:31:51 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
dhcp6_sendinform(void *arg)
|
|
|
|
{
|
|
|
|
|
|
|
|
dhcp6_sendmessage(arg, dhcp6_sendinform);
|
|
|
|
}
|
|
|
|
|
2012-11-07 07:40:15 +08:00
|
|
|
static void
|
|
|
|
dhcp6_senddiscover(void *arg)
|
|
|
|
{
|
|
|
|
|
|
|
|
dhcp6_sendmessage(arg, dhcp6_senddiscover);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
dhcp6_sendrequest(void *arg)
|
|
|
|
{
|
|
|
|
|
|
|
|
dhcp6_sendmessage(arg, dhcp6_sendrequest);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
dhcp6_sendrebind(void *arg)
|
|
|
|
{
|
|
|
|
|
|
|
|
dhcp6_sendmessage(arg, dhcp6_sendrebind);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
dhcp6_sendrenew(void *arg)
|
|
|
|
{
|
|
|
|
|
|
|
|
dhcp6_sendmessage(arg, dhcp6_sendrenew);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
dhcp6_sendconfirm(void *arg)
|
|
|
|
{
|
|
|
|
|
|
|
|
dhcp6_sendmessage(arg, dhcp6_sendconfirm);
|
|
|
|
}
|
|
|
|
|
2013-04-05 05:59:02 +08:00
|
|
|
/*
|
|
|
|
static void
|
|
|
|
dhcp6_sendrelease(void *arg)
|
|
|
|
{
|
|
|
|
|
|
|
|
dhcp6_sendmessage(arg, dhcp6_sendrelease);
|
|
|
|
}
|
|
|
|
*/
|
|
|
|
|
2012-11-07 07:40:15 +08:00
|
|
|
static void
|
|
|
|
dhcp6_startrenew(void *arg)
|
|
|
|
{
|
|
|
|
struct interface *ifp;
|
|
|
|
struct dhcp6_state *state;
|
|
|
|
|
|
|
|
ifp = arg;
|
|
|
|
state = D6_STATE(ifp);
|
|
|
|
state->state = DH6S_RENEW;
|
|
|
|
state->start_uptime = uptime();
|
|
|
|
state->RTC = 0;
|
|
|
|
state->IRT = REN_TIMEOUT;
|
|
|
|
state->MRT = REN_MAX_RT;
|
|
|
|
state->MRC = 0;
|
|
|
|
|
|
|
|
if (dhcp6_makemessage(ifp) == -1)
|
|
|
|
syslog(LOG_ERR, "%s: dhcp6_makemessage: %m", ifp->name);
|
|
|
|
else
|
|
|
|
dhcp6_sendrenew(ifp);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
dhcp6_startdiscover(void *arg)
|
|
|
|
{
|
|
|
|
struct interface *ifp;
|
|
|
|
struct dhcp6_state *state;
|
|
|
|
|
|
|
|
ifp = arg;
|
2013-06-12 06:49:07 +08:00
|
|
|
dhcp6_delete_delegates(ifp);
|
2013-06-11 14:22:02 +08:00
|
|
|
syslog(LOG_INFO, "%s: soliciting a DHCPv6 lease", ifp->name);
|
2012-11-07 07:40:15 +08:00
|
|
|
state = D6_STATE(ifp);
|
|
|
|
state->state = DH6S_DISCOVER;
|
|
|
|
state->start_uptime = uptime();
|
|
|
|
state->RTC = 0;
|
2013-06-12 23:01:59 +08:00
|
|
|
state->IMD = SOL_MAX_DELAY;
|
2012-11-07 07:40:15 +08:00
|
|
|
state->IRT = SOL_TIMEOUT;
|
Add support for the following RFCs:
DHCP SLP Directory Agent, RFC2610
DHCP Name Service Search, RFC2937
DHCP PANA Authentication Agent, RFC5192
DHCP Lost Server, RFC5223
DHCP CAPWAP, RFC5417
DHCP Mobility Services, RFC5678
DHCP SIP UA, RFC6011
DHCP ANDSF, RFC6153
DHCP RDNSS Selection for MIF Nodes, RFC6731
DHCP TFTP Server Address, RFC5859
DHCP PXELINUX, RFC5071
DHCP Access Network Domain Name, RFC5986
DHCP Virtual Subnet Selection, RFC6607
DHCP Relay Agent Remote-ID, RFC4649
DHCP Relay Agent Subscriber-ID, RFC4580
DHCPv6 Relay-ID, RFC5460
DHCPv6 LIS Discovery, RFC5986
DHCPv6 SIP UA, RFC6011
DHCPv6 Network Boot, RFC5970
DHCPv6 Home Info Discovery in MIPv6, RFC6610
DHCPv6 RDNSS Selection for MIF Nodes, RFC6731
DHCPv6 Kerberos, RFC6784
DHCPv6 Relay-Triggered Reconfiguraion, RFC6977
DHCPv6 SOL_MAX_RT, RFC7083
2014-02-03 18:28:30 +08:00
|
|
|
state->MRT = state->sol_max_rt;
|
2012-11-07 07:40:15 +08:00
|
|
|
state->MRC = 0;
|
|
|
|
|
2014-02-12 08:39:46 +08:00
|
|
|
eloop_timeout_delete(ifp->ctx->eloop, NULL, ifp);
|
2012-11-07 07:40:15 +08:00
|
|
|
free(state->new);
|
|
|
|
state->new = NULL;
|
|
|
|
state->new_len = 0;
|
|
|
|
|
2013-06-11 05:54:00 +08:00
|
|
|
dhcp6_freedrop_addrs(ifp, 0, NULL);
|
2013-06-11 16:55:58 +08:00
|
|
|
unlink(state->leasefile);
|
2012-11-25 04:11:49 +08:00
|
|
|
|
2012-11-07 07:40:15 +08:00
|
|
|
if (dhcp6_makemessage(ifp) == -1)
|
|
|
|
syslog(LOG_ERR, "%s: dhcp6_makemessage: %m", ifp->name);
|
|
|
|
else
|
|
|
|
dhcp6_senddiscover(ifp);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
dhcp6_failconfirm(void *arg)
|
|
|
|
{
|
|
|
|
struct interface *ifp;
|
|
|
|
|
|
|
|
ifp = arg;
|
2013-06-11 05:54:00 +08:00
|
|
|
syslog(LOG_ERR, "%s: failed to confirm prior address", ifp->name);
|
2012-11-07 07:40:15 +08:00
|
|
|
/* Section 18.1.2 says that we SHOULD use the last known
|
|
|
|
* IP address(s) and lifetimes if we didn't get a reply.
|
|
|
|
* I disagree with this. */
|
2013-06-11 04:31:34 +08:00
|
|
|
dhcp6_startdiscover(ifp);
|
2012-11-07 07:40:15 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
dhcp6_failrequest(void *arg)
|
|
|
|
{
|
|
|
|
struct interface *ifp;
|
|
|
|
|
|
|
|
ifp = arg;
|
|
|
|
syslog(LOG_ERR, "%s: failed to request address", ifp->name);
|
|
|
|
/* Section 18.1.1 says that client local policy dictates
|
|
|
|
* what happens if a REQUEST fails.
|
|
|
|
* Of the possible scenarios listed, moving back to the
|
|
|
|
* DISCOVER phase makes more sense for us. */
|
|
|
|
dhcp6_startdiscover(ifp);
|
|
|
|
}
|
|
|
|
|
2013-06-11 05:54:00 +08:00
|
|
|
static void
|
|
|
|
dhcp6_failrebind(void *arg)
|
|
|
|
{
|
|
|
|
struct interface *ifp;
|
|
|
|
|
|
|
|
ifp = arg;
|
|
|
|
syslog(LOG_ERR, "%s: failed to rebind prior delegation", ifp->name);
|
|
|
|
dhcp6_delete_delegates(ifp);
|
|
|
|
/* Section 18.1.2 says that we SHOULD use the last known
|
|
|
|
* IP address(s) and lifetimes if we didn't get a reply.
|
|
|
|
* I disagree with this. */
|
|
|
|
dhcp6_startdiscover(ifp);
|
|
|
|
}
|
|
|
|
|
2013-06-10 05:51:52 +08:00
|
|
|
static void
|
|
|
|
dhcp6_startrebind(void *arg)
|
|
|
|
{
|
|
|
|
struct interface *ifp;
|
|
|
|
struct dhcp6_state *state;
|
|
|
|
|
|
|
|
ifp = arg;
|
2014-02-12 08:39:46 +08:00
|
|
|
eloop_timeout_delete(ifp->ctx->eloop, dhcp6_sendrenew, ifp);
|
2013-06-10 05:51:52 +08:00
|
|
|
state = D6_STATE(ifp);
|
|
|
|
if (state->state == DH6S_RENEW)
|
|
|
|
syslog(LOG_WARNING, "%s: failed to renew DHCPv6, rebinding",
|
|
|
|
ifp->name);
|
|
|
|
state->state = DH6S_REBIND;
|
|
|
|
state->RTC = 0;
|
|
|
|
state->MRC = 0;
|
|
|
|
|
|
|
|
/* RFC 3633 section 12.1 */
|
|
|
|
if (ifp->options->ia_type == D6_OPTION_IA_PD) {
|
2013-10-10 18:09:48 +08:00
|
|
|
syslog(LOG_INFO, "%s: confirming Prefix Delegation", ifp->name);
|
2013-06-18 17:17:28 +08:00
|
|
|
state->IMD = CNF_MAX_DELAY;
|
|
|
|
state->IRT = CNF_TIMEOUT;
|
|
|
|
state->MRT = CNF_MAX_RT;
|
2013-06-10 05:51:52 +08:00
|
|
|
} else {
|
2013-06-18 17:17:28 +08:00
|
|
|
state->IRT = REB_TIMEOUT;
|
|
|
|
state->MRT = REB_MAX_RT;
|
2013-06-10 05:51:52 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
if (dhcp6_makemessage(ifp) == -1)
|
|
|
|
syslog(LOG_ERR, "%s: dhcp6_makemessage: %m", ifp->name);
|
|
|
|
else
|
|
|
|
dhcp6_sendrebind(ifp);
|
|
|
|
|
|
|
|
/* RFC 3633 section 12.1 */
|
|
|
|
if (ifp->options->ia_type == D6_OPTION_IA_PD)
|
2014-02-12 08:39:46 +08:00
|
|
|
eloop_timeout_add_sec(ifp->ctx->eloop,
|
|
|
|
CNF_MAX_RD, dhcp6_failrebind, ifp);
|
2013-06-10 05:51:52 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2012-11-07 07:40:15 +08:00
|
|
|
static void
|
|
|
|
dhcp6_startrequest(struct interface *ifp)
|
|
|
|
{
|
|
|
|
struct dhcp6_state *state;
|
|
|
|
|
2014-02-12 08:39:46 +08:00
|
|
|
eloop_timeout_delete(ifp->ctx->eloop, dhcp6_senddiscover, ifp);
|
2012-11-07 07:40:15 +08:00
|
|
|
state = D6_STATE(ifp);
|
|
|
|
state->state = DH6S_REQUEST;
|
|
|
|
state->RTC = 0;
|
|
|
|
state->IRT = REQ_TIMEOUT;
|
|
|
|
state->MRT = REQ_MAX_RT;
|
|
|
|
state->MRC = REQ_MAX_RC;
|
|
|
|
state->MRCcallback = dhcp6_failrequest;
|
|
|
|
|
|
|
|
if (dhcp6_makemessage(ifp) == -1) {
|
|
|
|
syslog(LOG_ERR, "%s: dhcp6_makemessage: %m", ifp->name);
|
|
|
|
return;
|
|
|
|
}
|
2014-01-25 09:35:53 +08:00
|
|
|
|
2012-11-07 07:40:15 +08:00
|
|
|
dhcp6_sendrequest(ifp);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
dhcp6_startconfirm(struct interface *ifp)
|
|
|
|
{
|
|
|
|
struct dhcp6_state *state;
|
|
|
|
|
|
|
|
state = D6_STATE(ifp);
|
2013-04-01 20:15:47 +08:00
|
|
|
state->state = DH6S_CONFIRM;
|
2012-11-07 07:40:15 +08:00
|
|
|
state->start_uptime = uptime();
|
|
|
|
state->RTC = 0;
|
2013-06-12 23:01:59 +08:00
|
|
|
state->IMD = CNF_MAX_DELAY;
|
2012-11-07 07:40:15 +08:00
|
|
|
state->IRT = CNF_TIMEOUT;
|
|
|
|
state->MRT = CNF_MAX_RT;
|
|
|
|
state->MRC = 0;
|
|
|
|
|
2014-01-25 09:35:53 +08:00
|
|
|
syslog(LOG_INFO, "%s: confirming prior DHCPv6 lease", ifp->name);
|
2012-11-07 07:40:15 +08:00
|
|
|
if (dhcp6_makemessage(ifp) == -1) {
|
|
|
|
syslog(LOG_ERR, "%s: dhcp6_makemessage: %m", ifp->name);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
dhcp6_sendconfirm(ifp);
|
2014-02-12 08:39:46 +08:00
|
|
|
eloop_timeout_add_sec(ifp->ctx->eloop,
|
|
|
|
CNF_MAX_RD, dhcp6_failconfirm, ifp);
|
2012-11-07 07:40:15 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2013-11-13 01:30:43 +08:00
|
|
|
dhcp6_startinform(void *arg)
|
2012-11-07 07:40:15 +08:00
|
|
|
{
|
2013-11-13 01:30:43 +08:00
|
|
|
struct interface *ifp;
|
2012-11-07 07:40:15 +08:00
|
|
|
struct dhcp6_state *state;
|
|
|
|
|
2013-11-13 01:30:43 +08:00
|
|
|
ifp = arg;
|
2012-11-07 07:40:15 +08:00
|
|
|
state = D6_STATE(ifp);
|
2013-06-12 18:58:28 +08:00
|
|
|
if (state->new == NULL || ifp->options->options & DHCPCD_DEBUG)
|
|
|
|
syslog(LOG_INFO, "%s: requesting DHCPv6 information",
|
|
|
|
ifp->name);
|
2012-11-07 07:40:15 +08:00
|
|
|
state->state = DH6S_INFORM;
|
|
|
|
state->start_uptime = uptime();
|
|
|
|
state->RTC = 0;
|
2013-06-12 23:01:59 +08:00
|
|
|
state->IMD = INF_MAX_DELAY;
|
2012-11-07 07:40:15 +08:00
|
|
|
state->IRT = INF_TIMEOUT;
|
Add support for the following RFCs:
DHCP SLP Directory Agent, RFC2610
DHCP Name Service Search, RFC2937
DHCP PANA Authentication Agent, RFC5192
DHCP Lost Server, RFC5223
DHCP CAPWAP, RFC5417
DHCP Mobility Services, RFC5678
DHCP SIP UA, RFC6011
DHCP ANDSF, RFC6153
DHCP RDNSS Selection for MIF Nodes, RFC6731
DHCP TFTP Server Address, RFC5859
DHCP PXELINUX, RFC5071
DHCP Access Network Domain Name, RFC5986
DHCP Virtual Subnet Selection, RFC6607
DHCP Relay Agent Remote-ID, RFC4649
DHCP Relay Agent Subscriber-ID, RFC4580
DHCPv6 Relay-ID, RFC5460
DHCPv6 LIS Discovery, RFC5986
DHCPv6 SIP UA, RFC6011
DHCPv6 Network Boot, RFC5970
DHCPv6 Home Info Discovery in MIPv6, RFC6610
DHCPv6 RDNSS Selection for MIF Nodes, RFC6731
DHCPv6 Kerberos, RFC6784
DHCPv6 Relay-Triggered Reconfiguraion, RFC6977
DHCPv6 SOL_MAX_RT, RFC7083
2014-02-03 18:28:30 +08:00
|
|
|
state->MRT = state->inf_max_rt;
|
2012-11-07 07:40:15 +08:00
|
|
|
state->MRC = 0;
|
|
|
|
|
|
|
|
if (dhcp6_makemessage(ifp) == -1)
|
|
|
|
syslog(LOG_ERR, "%s: dhcp6_makemessage: %m", ifp->name);
|
|
|
|
else
|
|
|
|
dhcp6_sendinform(ifp);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
dhcp6_startexpire(void *arg)
|
|
|
|
{
|
|
|
|
struct interface *ifp;
|
|
|
|
|
|
|
|
ifp = arg;
|
2014-02-12 08:39:46 +08:00
|
|
|
eloop_timeout_delete(ifp->ctx->eloop, dhcp6_sendrebind, ifp);
|
2012-11-07 07:40:15 +08:00
|
|
|
|
|
|
|
syslog(LOG_ERR, "%s: DHCPv6 lease expired", ifp->name);
|
2013-06-11 05:54:00 +08:00
|
|
|
dhcp6_freedrop_addrs(ifp, 1, NULL);
|
|
|
|
dhcp6_delete_delegates(ifp);
|
2013-02-02 22:05:55 +08:00
|
|
|
script_runreason(ifp, "EXPIRE6");
|
2012-11-07 07:40:15 +08:00
|
|
|
dhcp6_startdiscover(ifp);
|
|
|
|
}
|
|
|
|
|
2013-04-05 05:59:02 +08:00
|
|
|
static void
|
|
|
|
dhcp6_startrelease(struct interface *ifp)
|
|
|
|
{
|
|
|
|
struct dhcp6_state *state;
|
|
|
|
|
|
|
|
state = D6_STATE(ifp);
|
2013-05-31 06:21:42 +08:00
|
|
|
if (state->state != DH6S_BOUND)
|
|
|
|
return;
|
|
|
|
|
2013-04-05 05:59:02 +08:00
|
|
|
state->state = DH6S_RELEASE;
|
|
|
|
state->start_uptime = uptime();
|
|
|
|
state->RTC = 0;
|
|
|
|
state->IRT = REL_TIMEOUT;
|
|
|
|
state->MRT = 0;
|
|
|
|
state->MRC = REL_MAX_RC;
|
|
|
|
//state->MRCcallback = dhcp6_failrelease;
|
|
|
|
state->MRCcallback = NULL;
|
|
|
|
|
|
|
|
if (dhcp6_makemessage(ifp) == -1)
|
|
|
|
syslog(LOG_ERR, "%s: dhcp6_makemessage: %m", ifp->name);
|
|
|
|
else
|
|
|
|
/* XXX: We should loop a few times
|
|
|
|
* Luckily RFC3315 section 18.1.6 says this is optional */
|
|
|
|
//dhcp6_sendrelease(ifp);
|
|
|
|
dhcp6_sendmessage(ifp, NULL);
|
|
|
|
}
|
|
|
|
|
2014-02-12 08:39:46 +08:00
|
|
|
static int
|
|
|
|
dhcp6_checkstatusok(const struct interface *ifp,
|
|
|
|
const struct dhcp6_message *m, const uint8_t *p, size_t len)
|
2012-11-07 07:40:15 +08:00
|
|
|
{
|
2014-02-12 08:39:46 +08:00
|
|
|
const struct dhcp6_option *o;
|
2014-01-30 02:33:43 +08:00
|
|
|
uint16_t code;
|
2014-02-12 08:39:46 +08:00
|
|
|
char *status;
|
|
|
|
|
|
|
|
if (p)
|
|
|
|
o = dhcp6_findoption(D6_OPTION_STATUS_CODE, p, len);
|
|
|
|
else
|
|
|
|
o = dhcp6_getmoption(D6_OPTION_STATUS_CODE, m, len);
|
|
|
|
if (o == NULL) {
|
|
|
|
//syslog(LOG_DEBUG, "%s: no status", ifp->name);
|
|
|
|
return 0;
|
|
|
|
}
|
2012-11-07 07:40:15 +08:00
|
|
|
|
|
|
|
len = ntohs(o->len);
|
2014-01-30 02:33:43 +08:00
|
|
|
if (len < sizeof(code)) {
|
2014-02-12 08:39:46 +08:00
|
|
|
syslog(LOG_ERR, "%s: status truncated", ifp->name);
|
2012-11-07 07:40:15 +08:00
|
|
|
return -1;
|
|
|
|
}
|
2014-02-12 08:39:46 +08:00
|
|
|
|
2014-01-30 02:33:43 +08:00
|
|
|
p = D6_COPTION_DATA(o);
|
|
|
|
memcpy(&code, p, sizeof(code));
|
|
|
|
code = ntohs(code);
|
2014-02-12 08:39:46 +08:00
|
|
|
if (code == D6_STATUS_OK)
|
|
|
|
return 1;
|
|
|
|
|
2014-01-30 02:33:43 +08:00
|
|
|
len -= sizeof(code);
|
|
|
|
|
|
|
|
if (len == 0) {
|
|
|
|
if (code < sizeof(dhcp6_statuses) / sizeof(char *)) {
|
|
|
|
p = (const uint8_t *)dhcp6_statuses[code];
|
|
|
|
len = strlen((const char *)p);
|
|
|
|
} else
|
|
|
|
p = NULL;
|
2014-02-12 08:39:46 +08:00
|
|
|
} else
|
|
|
|
p += sizeof(code);
|
2013-05-31 19:53:10 +08:00
|
|
|
|
2014-02-12 08:39:46 +08:00
|
|
|
status = malloc(len + 1);
|
|
|
|
if (status == NULL) {
|
|
|
|
syslog(LOG_ERR, "%s: %m", __func__);
|
2013-05-31 19:53:10 +08:00
|
|
|
return -1;
|
|
|
|
}
|
2014-02-28 05:11:43 +08:00
|
|
|
if (p)
|
|
|
|
memcpy(status, p, len);
|
2014-02-12 08:39:46 +08:00
|
|
|
status[len] = '\0';
|
|
|
|
syslog(LOG_ERR, "%s: DHCPv6 REPLY: %s", ifp->name, status);
|
|
|
|
free(status);
|
|
|
|
return -1;
|
2013-05-31 19:53:10 +08:00
|
|
|
}
|
|
|
|
|
2013-05-24 05:50:34 +08:00
|
|
|
static struct ipv6_addr *
|
2013-09-01 00:48:39 +08:00
|
|
|
dhcp6_findaddr(struct interface *ifp, const struct in6_addr *addr)
|
2012-11-07 07:40:15 +08:00
|
|
|
{
|
|
|
|
const struct dhcp6_state *state;
|
2013-05-24 05:50:34 +08:00
|
|
|
struct ipv6_addr *ap;
|
2012-11-07 07:40:15 +08:00
|
|
|
|
2013-05-24 05:50:34 +08:00
|
|
|
state = D6_CSTATE(ifp);
|
|
|
|
if (state) {
|
2012-11-07 07:40:15 +08:00
|
|
|
TAILQ_FOREACH(ap, &state->addrs, next) {
|
2013-09-01 00:48:39 +08:00
|
|
|
if (addr == NULL) {
|
|
|
|
if ((ap->flags &
|
|
|
|
(IPV6_AF_ADDED | IPV6_AF_DADCOMPLETED)) ==
|
|
|
|
(IPV6_AF_ADDED | IPV6_AF_DADCOMPLETED))
|
|
|
|
return ap;
|
|
|
|
} else if (IN6_ARE_ADDR_EQUAL(&ap->addr, addr))
|
2013-05-24 05:50:34 +08:00
|
|
|
return ap;
|
2012-11-07 07:40:15 +08:00
|
|
|
}
|
|
|
|
}
|
2013-05-24 05:50:34 +08:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
2014-02-12 08:39:46 +08:00
|
|
|
dhcp6_addrexists(struct dhcpcd_ctx *ctx, const struct ipv6_addr *addr)
|
2013-05-24 05:50:34 +08:00
|
|
|
{
|
|
|
|
struct interface *ifp;
|
|
|
|
|
2014-02-12 08:39:46 +08:00
|
|
|
TAILQ_FOREACH(ifp, ctx->ifaces, next) {
|
2013-09-01 00:48:39 +08:00
|
|
|
if (dhcp6_findaddr(ifp, addr == NULL ? NULL : &addr->addr))
|
2013-05-24 05:50:34 +08:00
|
|
|
return 1;
|
|
|
|
}
|
2012-11-07 07:40:15 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2013-05-03 20:11:49 +08:00
|
|
|
static void
|
|
|
|
dhcp6_dadcallback(void *arg)
|
|
|
|
{
|
|
|
|
struct ipv6_addr *ap = arg;
|
2013-05-16 18:31:21 +08:00
|
|
|
struct interface *ifp;
|
|
|
|
struct dhcp6_state *state;
|
|
|
|
int wascompleted;
|
2013-05-03 20:11:49 +08:00
|
|
|
|
2013-05-31 21:01:54 +08:00
|
|
|
wascompleted = (ap->flags & IPV6_AF_DADCOMPLETED);
|
|
|
|
ap->flags |= IPV6_AF_DADCOMPLETED;
|
|
|
|
if (ap->flags & IPV6_AF_DUPLICATED)
|
2013-05-03 20:11:49 +08:00
|
|
|
/* XXX FIXME
|
|
|
|
* We should decline the address */
|
|
|
|
syslog(LOG_WARNING, "%s: DAD detected %s",
|
|
|
|
ap->iface->name, ap->saddr);
|
2013-05-16 18:31:21 +08:00
|
|
|
|
|
|
|
if (!wascompleted) {
|
|
|
|
ifp = ap->iface;
|
|
|
|
state = D6_STATE(ifp);
|
2013-05-29 20:59:47 +08:00
|
|
|
if (state->state == DH6S_BOUND ||
|
|
|
|
state->state == DH6S_DELEGATED)
|
|
|
|
{
|
2013-05-16 18:31:21 +08:00
|
|
|
TAILQ_FOREACH(ap, &state->addrs, next) {
|
2013-05-31 21:01:54 +08:00
|
|
|
if ((ap->flags & IPV6_AF_DADCOMPLETED) == 0) {
|
2013-05-16 18:31:21 +08:00
|
|
|
wascompleted = 1;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (!wascompleted) {
|
2013-05-24 05:50:34 +08:00
|
|
|
syslog(LOG_DEBUG, "%s: DHCPv6 DAD completed",
|
2013-05-16 18:31:21 +08:00
|
|
|
ifp->name);
|
|
|
|
script_runreason(ifp, state->reason);
|
2014-02-12 08:39:46 +08:00
|
|
|
daemonise(ifp->ctx);
|
2013-05-16 18:31:21 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2013-05-03 20:11:49 +08:00
|
|
|
}
|
|
|
|
|
2012-11-07 07:40:15 +08:00
|
|
|
static int
|
2013-04-01 20:15:47 +08:00
|
|
|
dhcp6_findna(struct interface *ifp, const uint8_t *iaid,
|
|
|
|
const uint8_t *d, size_t l)
|
2012-11-07 07:40:15 +08:00
|
|
|
{
|
|
|
|
struct dhcp6_state *state;
|
|
|
|
const struct dhcp6_option *o;
|
|
|
|
const uint8_t *p;
|
2013-05-24 05:50:34 +08:00
|
|
|
struct in6_addr in6;
|
2012-11-07 07:40:15 +08:00
|
|
|
struct ipv6_addr *a;
|
|
|
|
char iabuf[INET6_ADDRSTRLEN];
|
|
|
|
const char *ia;
|
|
|
|
int i;
|
|
|
|
uint32_t u32;
|
|
|
|
|
|
|
|
i = 0;
|
|
|
|
state = D6_STATE(ifp);
|
|
|
|
while ((o = dhcp6_findoption(D6_OPTION_IA_ADDR, d, l))) {
|
2013-04-05 04:31:04 +08:00
|
|
|
l -= ((const uint8_t *)o - d);
|
|
|
|
d += ((const uint8_t *)o - d);
|
2013-12-04 21:21:45 +08:00
|
|
|
u32 = ntohs(o->len);
|
2013-04-05 04:31:04 +08:00
|
|
|
l -= sizeof(*o) + u32;
|
|
|
|
d += sizeof(*o) + u32;
|
|
|
|
if (u32 < 24) {
|
|
|
|
errno = EINVAL;
|
|
|
|
syslog(LOG_ERR, "%s: IA Address option truncated",
|
|
|
|
ifp->name);
|
|
|
|
continue;
|
|
|
|
}
|
2013-05-24 05:50:34 +08:00
|
|
|
p = D6_COPTION_DATA(o);
|
|
|
|
memcpy(&in6.s6_addr, p, sizeof(in6.s6_addr));
|
|
|
|
p += sizeof(in6.s6_addr);
|
2013-09-01 00:48:39 +08:00
|
|
|
a = dhcp6_findaddr(ifp, &in6);
|
2013-04-01 20:15:47 +08:00
|
|
|
if (a == NULL) {
|
2013-05-24 05:50:34 +08:00
|
|
|
a = calloc(1, sizeof(*a));
|
|
|
|
if (a == NULL) {
|
|
|
|
syslog(LOG_ERR, "%s: %m", __func__);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
a->iface = ifp;
|
2013-05-31 21:01:54 +08:00
|
|
|
a->flags = IPV6_AF_NEW | IPV6_AF_ONLINK;
|
2013-05-24 05:50:34 +08:00
|
|
|
a->dadcallback = dhcp6_dadcallback;
|
|
|
|
memcpy(a->iaid, iaid, sizeof(a->iaid));
|
|
|
|
memcpy(&a->addr.s6_addr, &in6.s6_addr,
|
|
|
|
sizeof(in6.s6_addr));
|
2013-10-10 18:09:48 +08:00
|
|
|
|
|
|
|
/*
|
|
|
|
* RFC 5942 Section 5
|
|
|
|
* We cannot assume any prefix length, nor tie the
|
|
|
|
* address to an existing one as it could expire
|
|
|
|
* before the address.
|
|
|
|
* As such we just give it a 128 prefix.
|
|
|
|
*/
|
|
|
|
a->prefix_len = 128;
|
|
|
|
if (ipv6_makeprefix(&a->prefix, &a->addr,
|
|
|
|
a->prefix_len) == -1)
|
|
|
|
{
|
|
|
|
syslog(LOG_ERR, "%s: %m", __func__);
|
|
|
|
free(a);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
ia = inet_ntop(AF_INET6, &a->addr.s6_addr,
|
|
|
|
iabuf, sizeof(iabuf));
|
|
|
|
snprintf(a->saddr, sizeof(a->saddr),
|
|
|
|
"%s/%d", ia, a->prefix_len);
|
2014-02-28 18:34:05 +08:00
|
|
|
TAILQ_INSERT_TAIL(&state->addrs, a, next);
|
|
|
|
} else
|
|
|
|
a->flags &= ~IPV6_AF_STALE;
|
2013-04-01 20:15:47 +08:00
|
|
|
memcpy(&u32, p, sizeof(u32));
|
|
|
|
a->prefix_pltime = ntohl(u32);
|
|
|
|
p += sizeof(u32);
|
|
|
|
memcpy(&u32, p, sizeof(u32));
|
2013-10-10 18:09:48 +08:00
|
|
|
u32 = ntohl(u32);
|
|
|
|
if (a->prefix_vltime != u32) {
|
|
|
|
a->flags |= IPV6_AF_NEW;
|
|
|
|
a->prefix_vltime = u32;
|
|
|
|
}
|
2013-06-04 21:39:02 +08:00
|
|
|
if (a->prefix_pltime && a->prefix_pltime < state->lowpl)
|
2013-04-01 20:15:47 +08:00
|
|
|
state->lowpl = a->prefix_pltime;
|
2013-06-04 21:39:02 +08:00
|
|
|
if (a->prefix_vltime && a->prefix_vltime > state->expire)
|
2013-04-01 20:15:47 +08:00
|
|
|
state->expire = a->prefix_vltime;
|
|
|
|
i++;
|
|
|
|
}
|
|
|
|
return i;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
dhcp6_findpd(struct interface *ifp, const uint8_t *iaid,
|
|
|
|
const uint8_t *d, size_t l)
|
|
|
|
{
|
|
|
|
struct dhcp6_state *state;
|
|
|
|
const struct dhcp6_option *o;
|
|
|
|
const uint8_t *p;
|
|
|
|
struct ipv6_addr *a;
|
|
|
|
char iabuf[INET6_ADDRSTRLEN];
|
|
|
|
const char *ia;
|
|
|
|
int i;
|
2013-10-10 18:09:48 +08:00
|
|
|
uint8_t u8, len;
|
|
|
|
uint32_t u32, pltime, vltime;
|
|
|
|
struct in6_addr prefix;
|
2013-04-01 20:15:47 +08:00
|
|
|
|
|
|
|
i = 0;
|
|
|
|
state = D6_STATE(ifp);
|
|
|
|
while ((o = dhcp6_findoption(D6_OPTION_IAPREFIX, d, l))) {
|
2013-04-05 04:31:04 +08:00
|
|
|
l -= ((const uint8_t *)o - d);
|
|
|
|
d += ((const uint8_t *)o - d);
|
2013-12-04 21:21:45 +08:00
|
|
|
u32 = ntohs(o->len);
|
2013-04-05 04:31:04 +08:00
|
|
|
l -= sizeof(*o) + u32;
|
|
|
|
d += sizeof(*o) + u32;
|
|
|
|
if (u32 < 25) {
|
|
|
|
errno = EINVAL;
|
|
|
|
syslog(LOG_ERR, "%s: IA Prefix option truncated",
|
|
|
|
ifp->name);
|
|
|
|
continue;
|
|
|
|
}
|
2013-04-01 20:15:47 +08:00
|
|
|
p = D6_COPTION_DATA(o);
|
|
|
|
memcpy(&u32, p, sizeof(u32));
|
2013-10-10 18:09:48 +08:00
|
|
|
pltime = ntohl(u32);
|
2013-04-01 20:15:47 +08:00
|
|
|
p += sizeof(u32);
|
|
|
|
memcpy(&u32, p, sizeof(u32));
|
|
|
|
p += sizeof(u32);
|
2013-10-10 18:09:48 +08:00
|
|
|
vltime = ntohl(u32);
|
|
|
|
memcpy(&u8, p, sizeof(u8));
|
|
|
|
p += sizeof(u8);
|
|
|
|
len = u8;
|
|
|
|
memcpy(&prefix.s6_addr, p, sizeof(prefix.s6_addr));
|
|
|
|
|
|
|
|
TAILQ_FOREACH(a, &state->addrs, next) {
|
|
|
|
if (IN6_ARE_ADDR_EQUAL(&a->prefix, &prefix))
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (a == NULL) {
|
|
|
|
a = calloc(1, sizeof(*a));
|
|
|
|
if (a == NULL) {
|
|
|
|
syslog(LOG_ERR, "%s: %m", __func__);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
a->iface = ifp;
|
|
|
|
a->flags = IPV6_AF_NEW;
|
|
|
|
a->dadcallback = dhcp6_dadcallback;
|
|
|
|
memcpy(a->iaid, iaid, sizeof(a->iaid));
|
|
|
|
memcpy(&a->prefix.s6_addr,
|
|
|
|
&prefix.s6_addr, sizeof(a->prefix.s6_addr));
|
|
|
|
a->prefix_len = len;
|
|
|
|
ia = inet_ntop(AF_INET6, &a->prefix.s6_addr,
|
|
|
|
iabuf, sizeof(iabuf));
|
|
|
|
snprintf(a->saddr, sizeof(a->saddr),
|
|
|
|
"%s/%d", ia, a->prefix_len);
|
2014-02-28 18:34:05 +08:00
|
|
|
TAILQ_INSERT_TAIL(&state->addrs, a, next);
|
|
|
|
} else {
|
|
|
|
a->flags &= ~IPV6_AF_STALE;
|
|
|
|
if (a->prefix_vltime != vltime)
|
|
|
|
a->flags |= IPV6_AF_NEW;
|
|
|
|
}
|
2013-10-10 18:09:48 +08:00
|
|
|
|
|
|
|
a->prefix_pltime = pltime;
|
|
|
|
a->prefix_vltime = vltime;
|
2013-06-04 21:39:02 +08:00
|
|
|
if (a->prefix_pltime && a->prefix_pltime < state->lowpl)
|
2013-04-01 20:15:47 +08:00
|
|
|
state->lowpl = a->prefix_pltime;
|
2013-06-04 21:39:02 +08:00
|
|
|
if (a->prefix_vltime && a->prefix_vltime > state->expire)
|
2013-04-01 20:15:47 +08:00
|
|
|
state->expire = a->prefix_vltime;
|
|
|
|
i++;
|
|
|
|
}
|
|
|
|
return i;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
dhcp6_findia(struct interface *ifp, const uint8_t *d, size_t l,
|
|
|
|
const char *sfrom)
|
|
|
|
{
|
|
|
|
struct dhcp6_state *state;
|
|
|
|
const struct if_options *ifo;
|
|
|
|
const struct dhcp6_option *o;
|
|
|
|
const uint8_t *p;
|
2014-03-10 23:28:49 +08:00
|
|
|
int i, e;
|
2013-04-01 20:15:47 +08:00
|
|
|
uint32_t u32, renew, rebind;
|
|
|
|
uint8_t iaid[4];
|
|
|
|
size_t ol;
|
2013-05-24 05:50:34 +08:00
|
|
|
struct ipv6_addr *ap, *nap;
|
2013-04-01 20:15:47 +08:00
|
|
|
|
|
|
|
ifo = ifp->options;
|
2014-03-10 23:28:49 +08:00
|
|
|
i = e = 0;
|
2013-04-01 20:15:47 +08:00
|
|
|
state = D6_STATE(ifp);
|
2013-10-10 18:09:48 +08:00
|
|
|
TAILQ_FOREACH(ap, &state->addrs, next) {
|
|
|
|
ap->flags |= IPV6_AF_STALE;
|
2013-09-08 07:16:36 +08:00
|
|
|
}
|
2013-04-01 20:15:47 +08:00
|
|
|
while ((o = dhcp6_findoption(ifo->ia_type, d, l))) {
|
2013-04-05 04:31:04 +08:00
|
|
|
l -= ((const uint8_t *)o - d);
|
|
|
|
d += ((const uint8_t *)o - d);
|
2013-12-04 21:21:45 +08:00
|
|
|
ol = ntohs(o->len);
|
2013-04-05 04:31:04 +08:00
|
|
|
l -= sizeof(*o) + ol;
|
|
|
|
d += sizeof(*o) + ol;
|
|
|
|
u32 = ifo->ia_type == D6_OPTION_IA_TA ? 4 : 12;
|
|
|
|
if (ol < u32) {
|
|
|
|
errno = EINVAL;
|
|
|
|
syslog(LOG_ERR, "%s: IA option truncated", ifp->name);
|
|
|
|
continue;
|
|
|
|
}
|
2013-04-01 20:15:47 +08:00
|
|
|
|
|
|
|
p = D6_COPTION_DATA(o);
|
|
|
|
memcpy(iaid, p, sizeof(iaid));
|
|
|
|
p += sizeof(iaid);
|
|
|
|
ol -= sizeof(iaid);
|
2013-04-01 20:24:39 +08:00
|
|
|
if (ifo->ia_type != D6_OPTION_IA_TA) {
|
2012-11-07 07:40:15 +08:00
|
|
|
memcpy(&u32, p, sizeof(u32));
|
2013-04-01 20:15:47 +08:00
|
|
|
renew = ntohl(u32);
|
2012-11-07 07:40:15 +08:00
|
|
|
p += sizeof(u32);
|
2013-04-01 20:15:47 +08:00
|
|
|
ol -= sizeof(u32);
|
2012-11-07 07:40:15 +08:00
|
|
|
memcpy(&u32, p, sizeof(u32));
|
2013-04-01 20:15:47 +08:00
|
|
|
rebind = ntohl(u32);
|
|
|
|
p += sizeof(u32);
|
|
|
|
ol -= sizeof(u32);
|
2014-02-28 18:38:29 +08:00
|
|
|
} else
|
|
|
|
renew = rebind = 0; /* appease gcc */
|
2014-03-10 23:28:49 +08:00
|
|
|
if (dhcp6_checkstatusok(ifp, NULL, p, ol) == -1) {
|
|
|
|
e = 1;
|
2014-02-28 18:34:05 +08:00
|
|
|
continue;
|
2014-03-10 23:28:49 +08:00
|
|
|
}
|
2014-02-28 18:34:05 +08:00
|
|
|
if (ifo->ia_type == D6_OPTION_IA_PD) {
|
|
|
|
if (dhcp6_findpd(ifp, iaid, p, ol) == 0) {
|
|
|
|
syslog(LOG_WARNING,
|
|
|
|
"%s: %s: DHCPv6 REPLY missing Prefix",
|
|
|
|
ifp->name, sfrom);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if (dhcp6_findna(ifp, iaid, p, ol) == 0) {
|
|
|
|
syslog(LOG_WARNING,
|
|
|
|
"%s: %s: DHCPv6 REPLY missing IA Address",
|
|
|
|
ifp->name, sfrom);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (ifo->ia_type != D6_OPTION_IA_TA) {
|
2013-04-01 20:15:47 +08:00
|
|
|
if (renew > rebind && rebind > 0) {
|
|
|
|
if (sfrom)
|
|
|
|
syslog(LOG_WARNING,
|
|
|
|
"%s: T1 (%d) > T2 (%d) from %s",
|
|
|
|
ifp->name, renew, rebind, sfrom);
|
|
|
|
renew = 0;
|
|
|
|
rebind = 0;
|
|
|
|
}
|
|
|
|
if (renew != 0 &&
|
|
|
|
(renew < state->renew || state->renew == 0))
|
|
|
|
state->renew = renew;
|
|
|
|
if (rebind != 0 &&
|
|
|
|
(rebind < state->rebind || state->rebind == 0))
|
|
|
|
state->rebind = rebind;
|
|
|
|
}
|
|
|
|
i++;
|
2012-11-07 07:40:15 +08:00
|
|
|
}
|
2013-10-10 18:09:48 +08:00
|
|
|
TAILQ_FOREACH_SAFE(ap, &state->addrs, next, nap) {
|
|
|
|
if (ap->flags & IPV6_AF_STALE) {
|
|
|
|
TAILQ_REMOVE(&state->addrs, ap, next);
|
|
|
|
if (ap->dadcallback)
|
2014-02-12 08:39:46 +08:00
|
|
|
eloop_q_timeout_delete(ap->iface->ctx->eloop,
|
2014-03-03 21:56:49 +08:00
|
|
|
0, NULL, ap);
|
2013-10-10 18:09:48 +08:00
|
|
|
free(ap);
|
2013-09-08 07:16:36 +08:00
|
|
|
}
|
|
|
|
}
|
2014-03-10 23:28:49 +08:00
|
|
|
if (i == 0 && e)
|
|
|
|
return -1;
|
2012-11-07 07:40:15 +08:00
|
|
|
return i;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
dhcp6_validatelease(struct interface *ifp,
|
|
|
|
const struct dhcp6_message *m, size_t len,
|
|
|
|
const char *sfrom)
|
|
|
|
{
|
|
|
|
struct dhcp6_state *state;
|
|
|
|
const struct dhcp6_option *o;
|
|
|
|
|
|
|
|
state = D6_STATE(ifp);
|
2013-11-28 04:21:17 +08:00
|
|
|
o = dhcp6_getmoption(ifp->options->ia_type, m, len);
|
2012-11-07 07:40:15 +08:00
|
|
|
if (o == NULL) {
|
2013-10-07 00:35:43 +08:00
|
|
|
if (sfrom &&
|
|
|
|
dhcp6_checkstatusok(ifp, m, NULL, len) != -1)
|
2013-04-01 20:15:47 +08:00
|
|
|
syslog(LOG_ERR, "%s: no IA in REPLY from %s",
|
2012-11-07 07:40:15 +08:00
|
|
|
ifp->name, sfrom);
|
|
|
|
return -1;
|
|
|
|
}
|
2013-04-01 20:15:47 +08:00
|
|
|
|
2013-06-02 15:12:26 +08:00
|
|
|
if (dhcp6_checkstatusok(ifp, m, NULL, len) == -1)
|
|
|
|
return -1;
|
|
|
|
|
2013-04-01 20:15:47 +08:00
|
|
|
state->renew = state->rebind = state->expire = 0;
|
2013-05-31 21:40:46 +08:00
|
|
|
state->lowpl = ND6_INFINITE_LIFETIME;
|
2013-04-01 20:15:47 +08:00
|
|
|
len -= (const char *)o - (const char *)m;
|
|
|
|
return dhcp6_findia(ifp, (const uint8_t *)o, len, sfrom);
|
2012-11-07 07:40:15 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static ssize_t
|
|
|
|
dhcp6_writelease(const struct interface *ifp)
|
|
|
|
{
|
|
|
|
const struct dhcp6_state *state;
|
|
|
|
int fd;
|
|
|
|
ssize_t bytes;
|
|
|
|
|
|
|
|
state = D6_CSTATE(ifp);
|
|
|
|
syslog(LOG_DEBUG, "%s: writing lease `%s'",
|
|
|
|
ifp->name, state->leasefile);
|
|
|
|
|
2013-03-27 23:19:48 +08:00
|
|
|
fd = open(state->leasefile, O_WRONLY | O_CREAT | O_TRUNC, 0644);
|
2012-11-07 07:40:15 +08:00
|
|
|
if (fd == -1) {
|
|
|
|
syslog(LOG_ERR, "%s: dhcp6_writelease: %m", ifp->name);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
bytes = write(fd, state->new, state->new_len);
|
|
|
|
close(fd);
|
|
|
|
return bytes;
|
|
|
|
}
|
|
|
|
|
2013-06-12 18:58:28 +08:00
|
|
|
static int
|
2012-11-07 07:40:15 +08:00
|
|
|
dhcp6_readlease(struct interface *ifp)
|
|
|
|
{
|
|
|
|
struct dhcp6_state *state;
|
|
|
|
struct stat st;
|
|
|
|
int fd;
|
|
|
|
ssize_t bytes;
|
|
|
|
struct timeval now;
|
2014-01-25 09:35:53 +08:00
|
|
|
const struct dhcp6_option *o;
|
2012-11-07 07:40:15 +08:00
|
|
|
|
|
|
|
state = D6_STATE(ifp);
|
|
|
|
if (stat(state->leasefile, &st) == -1) {
|
|
|
|
if (errno == ENOENT)
|
|
|
|
return 0;
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
syslog(LOG_DEBUG, "%s: reading lease `%s'",
|
|
|
|
ifp->name, state->leasefile);
|
|
|
|
state->new = malloc(st.st_size);
|
|
|
|
if (state->new == NULL)
|
|
|
|
return -1;
|
|
|
|
state->new_len = st.st_size;
|
|
|
|
fd = open(state->leasefile, O_RDONLY);
|
|
|
|
if (fd == -1)
|
|
|
|
return -1;
|
|
|
|
bytes = read(fd, state->new, state->new_len);
|
|
|
|
close(fd);
|
2013-06-12 23:21:05 +08:00
|
|
|
if (bytes < (ssize_t)state->new_len) {
|
|
|
|
syslog(LOG_ERR, "%s: read: %m", __func__);
|
|
|
|
goto ex;
|
|
|
|
}
|
2012-11-07 07:40:15 +08:00
|
|
|
|
|
|
|
/* Check to see if the lease is still valid */
|
2013-06-12 18:58:28 +08:00
|
|
|
fd = dhcp6_validatelease(ifp, state->new, state->new_len, NULL);
|
|
|
|
if (fd == -1)
|
|
|
|
goto ex;
|
|
|
|
if (fd == 0) {
|
|
|
|
syslog(LOG_INFO, "%s: lease was for different IA type",
|
|
|
|
ifp->name);
|
2012-11-07 07:40:15 +08:00
|
|
|
goto ex;
|
2013-06-12 18:58:28 +08:00
|
|
|
}
|
2012-11-07 07:40:15 +08:00
|
|
|
|
2013-05-31 21:40:46 +08:00
|
|
|
if (state->expire != ND6_INFINITE_LIFETIME) {
|
|
|
|
gettimeofday(&now, NULL);
|
|
|
|
if ((time_t)state->expire < now.tv_sec - st.st_mtime) {
|
|
|
|
syslog(LOG_DEBUG,"%s: discarding expired lease",
|
|
|
|
ifp->name);
|
|
|
|
goto ex;
|
|
|
|
}
|
2012-11-07 07:40:15 +08:00
|
|
|
}
|
|
|
|
|
2014-01-25 09:35:53 +08:00
|
|
|
/* Authenticate the message */
|
|
|
|
o = dhcp6_getmoption(D6_OPTION_AUTH, state->new, state->new_len);
|
|
|
|
if (o) {
|
|
|
|
if (dhcp_auth_validate(&state->auth, &ifp->options->auth,
|
|
|
|
(uint8_t *)state->new, state->new_len, 6, state->new->type,
|
|
|
|
D6_COPTION_DATA(o), ntohs(o->len)) == NULL)
|
|
|
|
{
|
|
|
|
syslog(LOG_DEBUG, "%s: dhcp_auth_validate: %m",
|
|
|
|
ifp->name);
|
|
|
|
syslog(LOG_ERR, "%s: authentication failed",
|
|
|
|
ifp->name);
|
|
|
|
goto ex;
|
|
|
|
}
|
2014-03-10 23:28:49 +08:00
|
|
|
if (state->auth.token)
|
|
|
|
syslog(LOG_DEBUG, "%s: validated using 0x%08" PRIu32,
|
|
|
|
ifp->name, state->auth.token->secretid);
|
|
|
|
else
|
|
|
|
syslog(LOG_DEBUG, "%s: accepted reconfigure key",
|
|
|
|
ifp->name);
|
2014-01-25 09:35:53 +08:00
|
|
|
} else if (ifp->options->auth.options & DHCPCD_AUTH_REQUIRE) {
|
|
|
|
syslog(LOG_ERR, "%s: authentication now required", ifp->name);
|
|
|
|
goto ex;
|
2014-01-25 10:41:42 +08:00
|
|
|
}
|
2014-01-25 09:35:53 +08:00
|
|
|
|
2013-06-12 18:58:28 +08:00
|
|
|
return fd;
|
2012-11-07 07:40:15 +08:00
|
|
|
|
|
|
|
ex:
|
2013-06-11 05:54:00 +08:00
|
|
|
dhcp6_freedrop_addrs(ifp, 0, NULL);
|
2012-11-07 07:40:15 +08:00
|
|
|
free(state->new);
|
|
|
|
state->new = NULL;
|
|
|
|
state->new_len = 0;
|
|
|
|
unlink(state->leasefile);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
dhcp6_startinit(struct interface *ifp)
|
|
|
|
{
|
|
|
|
struct dhcp6_state *state;
|
|
|
|
int r;
|
|
|
|
|
|
|
|
state = D6_STATE(ifp);
|
|
|
|
state->state = DH6S_INIT;
|
2013-05-31 21:40:46 +08:00
|
|
|
state->expire = ND6_INFINITE_LIFETIME;
|
|
|
|
state->lowpl = ND6_INFINITE_LIFETIME;
|
2014-02-12 08:39:46 +08:00
|
|
|
if (!(ifp->ctx->options & DHCPCD_TEST) &&
|
2013-04-05 22:55:50 +08:00
|
|
|
ifp->options->ia_type != D6_OPTION_IA_TA &&
|
|
|
|
ifp->options->reboot != 0)
|
2013-04-01 20:15:47 +08:00
|
|
|
{
|
2012-11-07 07:40:15 +08:00
|
|
|
r = dhcp6_readlease(ifp);
|
|
|
|
if (r == -1)
|
|
|
|
syslog(LOG_ERR, "%s: dhcp6_readlease: %s: %m",
|
|
|
|
ifp->name, state->leasefile);
|
|
|
|
else if (r != 0) {
|
2013-04-01 20:15:47 +08:00
|
|
|
/* RFC 3633 section 12.1 */
|
|
|
|
if (ifp->options->ia_type == D6_OPTION_IA_PD)
|
|
|
|
dhcp6_startrebind(ifp);
|
|
|
|
else
|
|
|
|
dhcp6_startconfirm(ifp);
|
2012-11-07 07:40:15 +08:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
dhcp6_startdiscover(ifp);
|
|
|
|
}
|
|
|
|
|
2013-06-04 06:02:51 +08:00
|
|
|
static uint32_t
|
2014-02-12 08:39:46 +08:00
|
|
|
dhcp6_findsla(const struct dhcpcd_ctx *ctx)
|
2013-06-04 06:02:51 +08:00
|
|
|
{
|
|
|
|
uint32_t sla;
|
|
|
|
const struct interface *ifp;
|
|
|
|
const struct dhcp6_state *state;
|
|
|
|
|
|
|
|
/* Slow, but finding the lowest free SLA is needed if we get a
|
|
|
|
* /62 or /63 prefix from upstream */
|
|
|
|
for (sla = 0; sla < UINT32_MAX; sla++) {
|
2014-02-12 08:39:46 +08:00
|
|
|
TAILQ_FOREACH(ifp, ctx->ifaces, next) {
|
2013-06-04 06:02:51 +08:00
|
|
|
state = D6_CSTATE(ifp);
|
|
|
|
if (state && state->sla_set && state->sla == sla)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (ifp == NULL)
|
|
|
|
return sla;
|
|
|
|
}
|
|
|
|
|
|
|
|
errno = E2BIG;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct ipv6_addr *
|
|
|
|
dhcp6_delegate_addr(struct interface *ifp, const struct ipv6_addr *prefix,
|
|
|
|
const struct if_sla *sla, struct interface *ifs)
|
2013-04-01 20:15:47 +08:00
|
|
|
{
|
|
|
|
struct dhcp6_state *state;
|
2013-06-04 06:02:51 +08:00
|
|
|
struct if_sla asla;
|
|
|
|
struct in6_addr addr;
|
2013-10-10 18:09:48 +08:00
|
|
|
struct ipv6_addr *a, *ap, *apn;
|
2013-06-04 06:02:51 +08:00
|
|
|
char iabuf[INET6_ADDRSTRLEN];
|
|
|
|
const char *ia;
|
2013-04-01 20:15:47 +08:00
|
|
|
|
2013-06-02 05:30:18 +08:00
|
|
|
state = D6_STATE(ifp);
|
2013-04-01 20:15:47 +08:00
|
|
|
if (state == NULL) {
|
|
|
|
ifp->if_data[IF_DATA_DHCP6] = calloc(1, sizeof(*state));
|
|
|
|
state = D6_STATE(ifp);
|
|
|
|
if (state == NULL) {
|
|
|
|
syslog(LOG_ERR, "%s: %m", __func__);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
TAILQ_INIT(&state->addrs);
|
|
|
|
state->state = DH6S_DELEGATED;
|
2013-05-29 20:59:47 +08:00
|
|
|
state->reason = "DELEGATED6";
|
2013-04-01 20:15:47 +08:00
|
|
|
}
|
2013-06-02 17:51:30 +08:00
|
|
|
|
2013-06-04 06:02:51 +08:00
|
|
|
if (sla == NULL || sla->sla_set == 0) {
|
|
|
|
if (!state->sla_set) {
|
|
|
|
errno = 0;
|
2014-02-12 08:39:46 +08:00
|
|
|
state->sla = dhcp6_findsla(ifp->ctx);
|
2013-06-04 06:02:51 +08:00
|
|
|
if (errno) {
|
|
|
|
syslog(LOG_ERR, "%s: dhcp6_find_sla: %m",
|
|
|
|
ifp->name);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
syslog(LOG_DEBUG, "%s: set SLA %d",
|
|
|
|
ifp->name, state->sla);
|
|
|
|
state->sla_set = 1;
|
|
|
|
}
|
|
|
|
asla.sla = state->sla;
|
|
|
|
asla.prefix_len = 64;
|
|
|
|
sla = &asla;
|
|
|
|
}
|
2013-06-02 17:51:30 +08:00
|
|
|
|
|
|
|
if (ipv6_userprefix(&prefix->prefix, prefix->prefix_len,
|
|
|
|
sla->sla, &addr, sla->prefix_len) == -1)
|
|
|
|
{
|
|
|
|
ia = inet_ntop(AF_INET6, &prefix->prefix.s6_addr,
|
|
|
|
iabuf, sizeof(iabuf));
|
|
|
|
syslog(LOG_ERR, "%s: invalid prefix %s/%d + %d/%d: %m",
|
|
|
|
ifp->name, ia, prefix->prefix_len,
|
|
|
|
sla->sla, sla->prefix_len);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2013-05-03 22:17:24 +08:00
|
|
|
a = calloc(1, sizeof(*a));
|
2013-04-01 20:15:47 +08:00
|
|
|
if (a == NULL) {
|
|
|
|
syslog(LOG_ERR, "%s: %m", __func__);
|
|
|
|
return NULL;
|
|
|
|
}
|
2013-05-03 20:11:49 +08:00
|
|
|
a->iface = ifp;
|
2013-05-31 21:01:54 +08:00
|
|
|
a->flags = IPV6_AF_NEW | IPV6_AF_ONLINK;
|
2013-05-03 20:11:49 +08:00
|
|
|
a->dadcallback = dhcp6_dadcallback;
|
2013-04-01 20:15:47 +08:00
|
|
|
a->delegating_iface = ifs;
|
|
|
|
memcpy(&a->iaid, &prefix->iaid, sizeof(a->iaid));
|
|
|
|
a->prefix_pltime = prefix->prefix_pltime;
|
|
|
|
a->prefix_vltime = prefix->prefix_vltime;
|
2013-06-02 05:30:18 +08:00
|
|
|
memcpy(&a->prefix.s6_addr, &addr.s6_addr, sizeof(a->prefix.s6_addr));
|
|
|
|
a->prefix_len = sla->prefix_len;
|
2013-04-01 20:15:47 +08:00
|
|
|
|
2013-05-18 07:09:36 +08:00
|
|
|
if (ipv6_makeaddr(&a->addr, ifp, &a->prefix, a->prefix_len) == -1)
|
2013-04-01 20:15:47 +08:00
|
|
|
{
|
2013-04-05 04:31:04 +08:00
|
|
|
ia = inet_ntop(AF_INET6, &a->addr.s6_addr,
|
|
|
|
iabuf, sizeof(iabuf));
|
|
|
|
syslog(LOG_ERR, "%s: %m (%s/%d)", __func__, ia, a->prefix_len);
|
2013-04-01 20:15:47 +08:00
|
|
|
free(a);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Remove any exiting address */
|
2013-10-10 18:09:48 +08:00
|
|
|
TAILQ_FOREACH_SAFE(ap, &state->addrs, next, apn) {
|
2013-05-24 05:50:34 +08:00
|
|
|
if (IN6_ARE_ADDR_EQUAL(&ap->addr, &a->addr)) {
|
2013-04-01 20:15:47 +08:00
|
|
|
TAILQ_REMOVE(&state->addrs, ap, next);
|
2013-06-04 21:39:02 +08:00
|
|
|
/* Keep our flags */
|
|
|
|
a->flags |= ap->flags;
|
2013-10-10 18:09:48 +08:00
|
|
|
a->flags &= ~IPV6_AF_NEW;
|
2013-04-01 20:15:47 +08:00
|
|
|
free(ap);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
ia = inet_ntop(AF_INET6, &a->addr.s6_addr, iabuf, sizeof(iabuf));
|
|
|
|
snprintf(a->saddr, sizeof(a->saddr), "%s/%d", ia, a->prefix_len);
|
|
|
|
TAILQ_INSERT_TAIL(&state->addrs, a, next);
|
|
|
|
return a;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
dhcp6_delegate_prefix(struct interface *ifp)
|
|
|
|
{
|
|
|
|
struct if_options *ifo;
|
|
|
|
struct dhcp6_state *state, *ifd_state;
|
|
|
|
struct ipv6_addr *ap;
|
|
|
|
size_t i, j, k;
|
2013-11-15 21:43:41 +08:00
|
|
|
struct if_ia *ia;
|
2013-06-04 06:02:51 +08:00
|
|
|
struct if_sla *sla;
|
2013-04-01 20:15:47 +08:00
|
|
|
struct interface *ifd;
|
2013-06-02 05:30:18 +08:00
|
|
|
uint8_t carrier_warned;
|
2013-04-01 20:15:47 +08:00
|
|
|
|
|
|
|
ifo = ifp->options;
|
|
|
|
state = D6_STATE(ifp);
|
2014-02-12 08:39:46 +08:00
|
|
|
TAILQ_FOREACH(ifd, ifp->ctx->ifaces, next) {
|
2013-04-01 20:15:47 +08:00
|
|
|
k = 0;
|
2013-06-02 05:30:18 +08:00
|
|
|
carrier_warned = 0;
|
2013-04-01 20:15:47 +08:00
|
|
|
TAILQ_FOREACH(ap, &state->addrs, next) {
|
2013-06-02 05:30:18 +08:00
|
|
|
if (ap->flags & IPV6_AF_NEW) {
|
|
|
|
ap->flags &= ~IPV6_AF_NEW;
|
|
|
|
syslog(LOG_DEBUG, "%s: delegated prefix %s",
|
|
|
|
ifp->name, ap->saddr);
|
|
|
|
}
|
2013-11-15 21:43:41 +08:00
|
|
|
for (i = 0; i < ifo->ia_len; i++) {
|
|
|
|
ia = &ifo->ia[i];
|
|
|
|
if (memcmp(ia->iaid, ap->iaid,
|
|
|
|
sizeof(ia->iaid)))
|
2013-04-01 20:15:47 +08:00
|
|
|
continue;
|
2013-11-15 21:43:41 +08:00
|
|
|
if (ia->sla_len == 0) {
|
2013-06-02 17:51:30 +08:00
|
|
|
/* no SLA configured, so lets
|
|
|
|
* automate it */
|
|
|
|
if (ifp == ifd)
|
|
|
|
continue;
|
|
|
|
if (ifd->carrier == LINK_DOWN) {
|
|
|
|
syslog(LOG_DEBUG,
|
|
|
|
"%s: has no carrier, cannot"
|
|
|
|
" delegate addresses",
|
|
|
|
ifd->name);
|
|
|
|
carrier_warned = 1;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (dhcp6_delegate_addr(ifd, ap,
|
2013-06-04 06:02:51 +08:00
|
|
|
NULL, ifp))
|
2013-06-02 17:51:30 +08:00
|
|
|
k++;
|
|
|
|
}
|
2013-11-15 21:43:41 +08:00
|
|
|
for (j = 0; j < ia->sla_len; j++) {
|
|
|
|
sla = &ia->sla[j];
|
2013-04-01 20:15:47 +08:00
|
|
|
if (strcmp(ifd->name, sla->ifname))
|
|
|
|
continue;
|
2013-06-02 05:30:18 +08:00
|
|
|
if (ifd->carrier == LINK_DOWN) {
|
|
|
|
syslog(LOG_DEBUG,
|
|
|
|
"%s: has no carrier, cannot"
|
|
|
|
" delegate addresses",
|
|
|
|
ifd->name);
|
|
|
|
carrier_warned = 1;
|
|
|
|
break;
|
|
|
|
}
|
2013-04-01 20:15:47 +08:00
|
|
|
if (dhcp6_delegate_addr(ifd, ap,
|
|
|
|
sla, ifp))
|
|
|
|
k++;
|
|
|
|
}
|
2013-06-02 05:30:18 +08:00
|
|
|
if (carrier_warned)
|
|
|
|
break;
|
2013-04-01 20:15:47 +08:00
|
|
|
}
|
2013-06-02 05:30:18 +08:00
|
|
|
if (carrier_warned)
|
|
|
|
break;
|
2013-04-01 20:15:47 +08:00
|
|
|
}
|
2013-06-02 05:30:18 +08:00
|
|
|
if (k && !carrier_warned) {
|
2013-04-01 20:15:47 +08:00
|
|
|
ifd_state = D6_STATE(ifd);
|
2014-03-07 03:11:55 +08:00
|
|
|
ipv6_addaddrs(&ifd_state->addrs);
|
2013-04-01 20:15:47 +08:00
|
|
|
}
|
|
|
|
}
|
2013-10-07 00:35:43 +08:00
|
|
|
|
|
|
|
/* Warn about configured interfaces for delegation that do not exist */
|
2013-11-15 21:43:41 +08:00
|
|
|
for (i = 0; i < ifo->ia_len; i++) {
|
|
|
|
ia = &ifo->ia[i];
|
|
|
|
for (j = 0; j < ia->sla_len; j++) {
|
|
|
|
sla = &ia->sla[j];
|
2013-10-07 00:35:43 +08:00
|
|
|
for (k = 0; k < i; j++)
|
2013-11-15 21:43:41 +08:00
|
|
|
if (strcmp(sla->ifname, ia->sla[j].ifname) == 0)
|
2013-10-07 00:35:43 +08:00
|
|
|
break;
|
2014-02-12 08:39:46 +08:00
|
|
|
if (j >= i &&
|
|
|
|
find_interface(ifp->ctx, sla->ifname) == NULL)
|
2013-10-07 00:35:43 +08:00
|
|
|
syslog(LOG_ERR,
|
|
|
|
"%s: interface does not exist"
|
|
|
|
" for delegation",
|
|
|
|
sla->ifname);
|
|
|
|
}
|
|
|
|
}
|
2013-04-01 20:15:47 +08:00
|
|
|
}
|
|
|
|
|
2013-06-02 05:30:18 +08:00
|
|
|
static void
|
|
|
|
dhcp6_find_delegates1(void *arg)
|
|
|
|
{
|
|
|
|
|
|
|
|
dhcp6_find_delegates(arg);
|
|
|
|
}
|
|
|
|
|
2013-04-01 20:15:47 +08:00
|
|
|
int
|
|
|
|
dhcp6_find_delegates(struct interface *ifp)
|
|
|
|
{
|
|
|
|
struct if_options *ifo;
|
|
|
|
struct dhcp6_state *state;
|
|
|
|
struct ipv6_addr *ap;
|
|
|
|
size_t i, j, k;
|
2013-11-15 21:43:41 +08:00
|
|
|
struct if_ia *ia;
|
2013-04-01 20:15:47 +08:00
|
|
|
struct if_sla *sla;
|
|
|
|
struct interface *ifd;
|
|
|
|
|
|
|
|
k = 0;
|
2014-02-12 08:39:46 +08:00
|
|
|
TAILQ_FOREACH(ifd, ifp->ctx->ifaces, next) {
|
2013-04-01 20:15:47 +08:00
|
|
|
ifo = ifd->options;
|
|
|
|
if (ifo->ia_type != D6_OPTION_IA_PD)
|
|
|
|
continue;
|
|
|
|
state = D6_STATE(ifd);
|
|
|
|
if (state == NULL || state->state != DH6S_BOUND)
|
|
|
|
continue;
|
|
|
|
TAILQ_FOREACH(ap, &state->addrs, next) {
|
2013-11-15 21:43:41 +08:00
|
|
|
for (i = 0; i < ifo->ia_len; i++) {
|
|
|
|
ia = &ifo->ia[i];
|
|
|
|
if (memcmp(ia->iaid, ap->iaid,
|
|
|
|
sizeof(ia->iaid)))
|
2013-04-01 20:15:47 +08:00
|
|
|
continue;
|
2013-11-15 21:43:41 +08:00
|
|
|
for (j = 0; j < ia->sla_len; j++) {
|
|
|
|
sla = &ia->sla[j];
|
2013-04-01 20:15:47 +08:00
|
|
|
if (strcmp(ifp->name, sla->ifname))
|
|
|
|
continue;
|
2013-06-02 05:30:18 +08:00
|
|
|
if (ipv6_linklocal(ifp) == NULL) {
|
|
|
|
syslog(LOG_DEBUG,
|
|
|
|
"%s: delaying adding"
|
|
|
|
" delegated addresses for"
|
|
|
|
" LL address",
|
|
|
|
ifp->name);
|
|
|
|
ipv6_addlinklocalcallback(ifp,
|
|
|
|
dhcp6_find_delegates1, ifp);
|
|
|
|
return 1;
|
|
|
|
}
|
2013-04-01 20:15:47 +08:00
|
|
|
if (dhcp6_delegate_addr(ifp, ap,
|
|
|
|
sla, ifd))
|
|
|
|
k++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (k) {
|
|
|
|
syslog(LOG_INFO, "%s: adding delegated prefixes", ifp->name);
|
|
|
|
state = D6_STATE(ifp);
|
2013-05-15 18:27:36 +08:00
|
|
|
state->state = DH6S_DELEGATED;
|
2014-03-07 03:11:55 +08:00
|
|
|
ipv6_addaddrs(&state->addrs);
|
2014-02-12 08:39:46 +08:00
|
|
|
ipv6_buildroutes(ifp->ctx);
|
2013-04-01 20:15:47 +08:00
|
|
|
}
|
|
|
|
return k;
|
|
|
|
}
|
|
|
|
|
2012-10-12 18:31:51 +08:00
|
|
|
/* ARGSUSED */
|
|
|
|
static void
|
2014-02-12 08:39:46 +08:00
|
|
|
dhcp6_handledata(void *arg)
|
2012-10-12 18:31:51 +08:00
|
|
|
{
|
2014-02-12 08:39:46 +08:00
|
|
|
struct dhcpcd_ctx *dhcpcd_ctx;
|
|
|
|
struct ipv6_ctx *ctx;
|
2012-10-12 18:31:51 +08:00
|
|
|
ssize_t len;
|
2013-12-04 07:10:21 +08:00
|
|
|
size_t i;
|
2012-10-12 18:31:51 +08:00
|
|
|
struct cmsghdr *cm;
|
|
|
|
struct in6_pktinfo pkt;
|
|
|
|
struct interface *ifp;
|
2014-02-12 08:39:46 +08:00
|
|
|
const char *op;
|
2012-11-07 07:40:15 +08:00
|
|
|
struct dhcp6_message *r;
|
2012-10-12 18:31:51 +08:00
|
|
|
struct dhcp6_state *state;
|
2014-01-31 22:25:18 +08:00
|
|
|
const struct dhcp6_option *o, *auth;
|
2012-10-12 18:31:51 +08:00
|
|
|
const struct dhcp_opt *opt;
|
|
|
|
const struct if_options *ifo;
|
2013-06-09 15:31:08 +08:00
|
|
|
struct ipv6_addr *ap;
|
2013-06-02 15:12:26 +08:00
|
|
|
uint8_t has_new;
|
|
|
|
int error;
|
2013-11-13 01:30:43 +08:00
|
|
|
uint32_t u32;
|
2012-10-12 18:31:51 +08:00
|
|
|
|
2014-02-12 08:39:46 +08:00
|
|
|
dhcpcd_ctx = arg;
|
|
|
|
ctx = dhcpcd_ctx->ipv6;
|
|
|
|
len = recvmsg(ctx->dhcp_fd, &ctx->rcvhdr, 0);
|
2012-10-12 18:31:51 +08:00
|
|
|
if (len == -1) {
|
|
|
|
syslog(LOG_ERR, "recvmsg: %m");
|
|
|
|
return;
|
|
|
|
}
|
2014-02-12 08:39:46 +08:00
|
|
|
ctx->sfrom = inet_ntop(AF_INET6, &ctx->from.sin6_addr,
|
|
|
|
ctx->ntopbuf, sizeof(ctx->ntopbuf));
|
2012-10-12 18:31:51 +08:00
|
|
|
if ((size_t)len < sizeof(struct dhcp6_message)) {
|
2014-02-12 08:39:46 +08:00
|
|
|
syslog(LOG_ERR, "DHCPv6 RA packet too short from %s",
|
|
|
|
ctx->sfrom);
|
2012-10-12 18:31:51 +08:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
pkt.ipi6_ifindex = 0;
|
2014-02-12 08:39:46 +08:00
|
|
|
for (cm = (struct cmsghdr *)CMSG_FIRSTHDR(&ctx->rcvhdr);
|
2012-10-12 18:31:51 +08:00
|
|
|
cm;
|
2014-02-12 08:39:46 +08:00
|
|
|
cm = (struct cmsghdr *)CMSG_NXTHDR(&ctx->rcvhdr, cm))
|
2012-10-12 18:31:51 +08:00
|
|
|
{
|
|
|
|
if (cm->cmsg_level != IPPROTO_IPV6)
|
|
|
|
continue;
|
|
|
|
switch(cm->cmsg_type) {
|
|
|
|
case IPV6_PKTINFO:
|
|
|
|
if (cm->cmsg_len == CMSG_LEN(sizeof(pkt)))
|
|
|
|
memcpy(&pkt, CMSG_DATA(cm), sizeof(pkt));
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (pkt.ipi6_ifindex == 0) {
|
|
|
|
syslog(LOG_ERR,
|
|
|
|
"DHCPv6 reply did not contain index from %s",
|
2014-02-12 08:39:46 +08:00
|
|
|
ctx->sfrom);
|
2012-10-12 18:31:51 +08:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2014-02-12 08:39:46 +08:00
|
|
|
TAILQ_FOREACH(ifp, dhcpcd_ctx->ifaces, next) {
|
2012-10-12 18:31:51 +08:00
|
|
|
if (ifp->index == (unsigned int)pkt.ipi6_ifindex)
|
|
|
|
break;
|
2013-02-19 21:37:42 +08:00
|
|
|
}
|
2012-10-12 18:31:51 +08:00
|
|
|
if (ifp == NULL) {
|
2013-12-13 17:11:42 +08:00
|
|
|
syslog(LOG_DEBUG,
|
2014-02-12 08:39:46 +08:00
|
|
|
"DHCPv6 reply for unexpected interface from %s",
|
|
|
|
ctx->sfrom);
|
2012-10-12 18:31:51 +08:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
state = D6_STATE(ifp);
|
|
|
|
if (state == NULL || state->send == NULL) {
|
2013-12-13 17:11:42 +08:00
|
|
|
syslog(LOG_DEBUG, "%s: DHCPv6 reply received but not running",
|
2012-10-12 18:31:51 +08:00
|
|
|
ifp->name);
|
|
|
|
return;
|
|
|
|
}
|
2014-03-10 23:28:49 +08:00
|
|
|
|
|
|
|
r = (struct dhcp6_message *)ctx->rcvhdr.msg_iov[0].iov_base;
|
|
|
|
|
2013-05-21 20:34:06 +08:00
|
|
|
/* We're already bound and this message is for another machine */
|
2013-06-12 23:01:59 +08:00
|
|
|
/* XXX DELEGATED? */
|
2014-03-10 23:28:49 +08:00
|
|
|
if (r->type != DHCP6_RECONFIGURE &&
|
|
|
|
(state->state == DH6S_BOUND || state->state == DH6S_INFORMED))
|
2013-05-21 20:34:06 +08:00
|
|
|
return;
|
2012-10-12 18:31:51 +08:00
|
|
|
|
2014-02-12 08:39:46 +08:00
|
|
|
r = (struct dhcp6_message *)ctx->rcvhdr.msg_iov[0].iov_base;
|
2014-01-31 22:25:18 +08:00
|
|
|
if (r->type != DHCP6_RECONFIGURE &&
|
|
|
|
(r->xid[0] != state->send->xid[0] ||
|
2012-11-07 07:40:15 +08:00
|
|
|
r->xid[1] != state->send->xid[1] ||
|
2014-01-31 22:25:18 +08:00
|
|
|
r->xid[2] != state->send->xid[2]))
|
2012-10-12 18:31:51 +08:00
|
|
|
{
|
2013-12-13 17:11:42 +08:00
|
|
|
syslog(LOG_DEBUG,
|
2013-05-15 18:27:36 +08:00
|
|
|
"%s: wrong xid 0x%02x%02x%02x"
|
|
|
|
" (expecting 0x%02x%02x%02x) from %s",
|
2012-10-12 18:31:51 +08:00
|
|
|
ifp->name,
|
|
|
|
r->xid[0], r->xid[1], r->xid[2],
|
2012-11-07 07:40:15 +08:00
|
|
|
state->send->xid[0], state->send->xid[1],
|
|
|
|
state->send->xid[2],
|
2014-02-12 08:39:46 +08:00
|
|
|
ctx->sfrom);
|
2012-10-12 18:31:51 +08:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2013-11-28 04:21:17 +08:00
|
|
|
if (dhcp6_getmoption(D6_OPTION_SERVERID, r, len) == NULL) {
|
2013-12-13 17:11:42 +08:00
|
|
|
syslog(LOG_DEBUG, "%s: no DHCPv6 server ID from %s",
|
2014-02-12 08:39:46 +08:00
|
|
|
ifp->name, ctx->sfrom);
|
2012-10-12 18:31:51 +08:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2013-11-28 04:21:17 +08:00
|
|
|
o = dhcp6_getmoption(D6_OPTION_CLIENTID, r, len);
|
2014-02-12 08:39:46 +08:00
|
|
|
if (o == NULL || ntohs(o->len) != dhcpcd_ctx->duid_len ||
|
|
|
|
memcmp(D6_COPTION_DATA(o),
|
|
|
|
dhcpcd_ctx->duid, dhcpcd_ctx->duid_len) != 0)
|
2012-10-12 18:31:51 +08:00
|
|
|
{
|
2013-12-13 17:11:42 +08:00
|
|
|
syslog(LOG_DEBUG, "%s: incorrect client ID from %s",
|
2014-02-12 08:39:46 +08:00
|
|
|
ifp->name, ctx->sfrom);
|
2012-10-12 18:31:51 +08:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2013-02-03 18:35:59 +08:00
|
|
|
ifo = ifp->options;
|
2014-02-12 08:39:46 +08:00
|
|
|
for (i = 0, opt = dhcpcd_ctx->dhcp6_opts;
|
|
|
|
i < dhcpcd_ctx->dhcp6_opts_len;
|
|
|
|
i++, opt++)
|
|
|
|
{
|
2012-10-12 18:31:51 +08:00
|
|
|
if (has_option_mask(ifo->requiremask6, opt->option) &&
|
2013-11-28 04:21:17 +08:00
|
|
|
dhcp6_getmoption(opt->option, r, len) == NULL)
|
2012-10-12 18:31:51 +08:00
|
|
|
{
|
|
|
|
syslog(LOG_WARNING,
|
|
|
|
"%s: reject DHCPv6 (no option %s) from %s",
|
2014-02-12 08:39:46 +08:00
|
|
|
ifp->name, opt->var, ctx->sfrom);
|
2012-10-12 18:31:51 +08:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-01-25 09:35:53 +08:00
|
|
|
/* Authenticate the message */
|
2014-01-31 22:25:18 +08:00
|
|
|
auth = dhcp6_getmoption(D6_OPTION_AUTH, r, len);
|
|
|
|
if (auth) {
|
2014-01-25 09:35:53 +08:00
|
|
|
if (dhcp_auth_validate(&state->auth, &ifo->auth,
|
|
|
|
(uint8_t *)r, len, 6, r->type,
|
2014-01-31 22:25:18 +08:00
|
|
|
D6_COPTION_DATA(auth), ntohs(auth->len)) == NULL)
|
2014-01-25 09:35:53 +08:00
|
|
|
{
|
|
|
|
syslog(LOG_DEBUG, "dhcp_auth_validate: %m");
|
|
|
|
syslog(LOG_ERR, "%s: authentication failed from %s",
|
2014-02-12 08:39:46 +08:00
|
|
|
ifp->name, ctx->sfrom);
|
2014-01-25 09:35:53 +08:00
|
|
|
return;
|
|
|
|
}
|
2014-03-10 23:28:49 +08:00
|
|
|
if (state->auth.token)
|
|
|
|
syslog(LOG_DEBUG, "%s: validated using 0x%08" PRIu32,
|
|
|
|
ifp->name, state->auth.token->secretid);
|
|
|
|
else
|
|
|
|
syslog(LOG_DEBUG, "%s: accepted reconfigure key",
|
|
|
|
ifp->name);
|
2014-01-25 09:35:53 +08:00
|
|
|
} else if (ifo->auth.options & DHCPCD_AUTH_REQUIRE) {
|
2014-01-30 22:20:24 +08:00
|
|
|
syslog(LOG_ERR, "%s: no authentication from %s",
|
2014-02-12 08:39:46 +08:00
|
|
|
ifp->name, ctx->sfrom);
|
2014-01-25 09:35:53 +08:00
|
|
|
return;
|
2014-01-25 10:41:42 +08:00
|
|
|
} else if (ifo->auth.options & DHCPCD_AUTH_SEND)
|
|
|
|
syslog(LOG_WARNING,
|
2014-01-30 22:20:24 +08:00
|
|
|
"%s: no authentication from %s",
|
2014-02-12 08:39:46 +08:00
|
|
|
ifp->name, ctx->sfrom);
|
2014-01-25 09:35:53 +08:00
|
|
|
|
2012-10-12 18:31:51 +08:00
|
|
|
op = dhcp6_get_op(r->type);
|
2012-11-07 07:40:15 +08:00
|
|
|
switch(r->type) {
|
|
|
|
case DHCP6_REPLY:
|
|
|
|
switch(state->state) {
|
2013-11-13 01:30:43 +08:00
|
|
|
case DH6S_INFORM:
|
|
|
|
/* RFC4242 */
|
2013-11-28 04:21:17 +08:00
|
|
|
o = dhcp6_getmoption(D6_OPTION_INFO_REFRESH_TIME,
|
2013-11-13 01:30:43 +08:00
|
|
|
r, len);
|
|
|
|
if (o == NULL || ntohs(o->len) != sizeof(u32))
|
|
|
|
state->renew = IRT_DEFAULT;
|
|
|
|
else {
|
|
|
|
memcpy(&u32, D6_COPTION_DATA(o), sizeof(u32));
|
|
|
|
state->renew = ntohl(u32);
|
|
|
|
if (state->renew < IRT_MINIMUM)
|
|
|
|
state->renew = IRT_MINIMUM;
|
|
|
|
}
|
|
|
|
break;
|
2013-04-01 20:15:47 +08:00
|
|
|
case DH6S_CONFIRM:
|
2013-06-02 15:12:26 +08:00
|
|
|
error = dhcp6_checkstatusok(ifp, r, NULL, len);
|
|
|
|
/* If we got an OK status the chances are that we
|
|
|
|
* didn't get the IA's returned, so preserve them
|
|
|
|
* from our saved response */
|
|
|
|
if (error == 1)
|
|
|
|
goto recv;
|
|
|
|
if (error == -1 ||
|
2014-03-10 23:28:49 +08:00
|
|
|
dhcp6_validatelease(ifp, r, len, ctx->sfrom) == -1)
|
|
|
|
{
|
2012-11-07 07:40:15 +08:00
|
|
|
dhcp6_startdiscover(ifp);
|
|
|
|
return;
|
|
|
|
}
|
2013-06-02 15:12:26 +08:00
|
|
|
break;
|
2013-06-06 07:57:03 +08:00
|
|
|
case DH6S_DISCOVER:
|
|
|
|
if (has_option_mask(ifo->requestmask6,
|
2013-11-13 01:30:43 +08:00
|
|
|
D6_OPTION_RAPID_COMMIT) &&
|
2013-11-28 04:21:17 +08:00
|
|
|
dhcp6_getmoption(D6_OPTION_RAPID_COMMIT, r, len))
|
2013-06-06 07:57:03 +08:00
|
|
|
state->state = DH6S_REQUEST;
|
|
|
|
else
|
|
|
|
op = NULL;
|
2012-11-07 07:40:15 +08:00
|
|
|
case DH6S_REQUEST: /* FALLTHROUGH */
|
|
|
|
case DH6S_RENEW: /* FALLTHROUGH */
|
|
|
|
case DH6S_REBIND:
|
2014-02-12 08:39:46 +08:00
|
|
|
if (dhcp6_validatelease(ifp, r, len, ctx->sfrom) == -1){
|
2013-06-11 16:55:58 +08:00
|
|
|
/* PD doesn't use CONFIRM, so REBIND could
|
|
|
|
* throw up an invalid prefix if we
|
|
|
|
* changed link */
|
|
|
|
if (ifp->options->ia_type == D6_OPTION_IA_PD)
|
|
|
|
dhcp6_startdiscover(ifp);
|
2013-06-02 15:12:26 +08:00
|
|
|
return;
|
2013-06-11 16:55:58 +08:00
|
|
|
}
|
2013-06-02 15:12:26 +08:00
|
|
|
break;
|
2012-11-07 07:40:15 +08:00
|
|
|
default:
|
|
|
|
op = NULL;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case DHCP6_ADVERTISE:
|
|
|
|
if (state->state != DH6S_DISCOVER) {
|
|
|
|
op = NULL;
|
|
|
|
break;
|
|
|
|
}
|
Add support for the following RFCs:
DHCP SLP Directory Agent, RFC2610
DHCP Name Service Search, RFC2937
DHCP PANA Authentication Agent, RFC5192
DHCP Lost Server, RFC5223
DHCP CAPWAP, RFC5417
DHCP Mobility Services, RFC5678
DHCP SIP UA, RFC6011
DHCP ANDSF, RFC6153
DHCP RDNSS Selection for MIF Nodes, RFC6731
DHCP TFTP Server Address, RFC5859
DHCP PXELINUX, RFC5071
DHCP Access Network Domain Name, RFC5986
DHCP Virtual Subnet Selection, RFC6607
DHCP Relay Agent Remote-ID, RFC4649
DHCP Relay Agent Subscriber-ID, RFC4580
DHCPv6 Relay-ID, RFC5460
DHCPv6 LIS Discovery, RFC5986
DHCPv6 SIP UA, RFC6011
DHCPv6 Network Boot, RFC5970
DHCPv6 Home Info Discovery in MIPv6, RFC6610
DHCPv6 RDNSS Selection for MIF Nodes, RFC6731
DHCPv6 Kerberos, RFC6784
DHCPv6 Relay-Triggered Reconfiguraion, RFC6977
DHCPv6 SOL_MAX_RT, RFC7083
2014-02-03 18:28:30 +08:00
|
|
|
/* RFC7083 */
|
|
|
|
o = dhcp6_getmoption(D6_OPTION_SOL_MAX_RT, r, len);
|
|
|
|
if (o && ntohs(o->len) >= sizeof(u32)) {
|
|
|
|
memcpy(&u32, D6_COPTION_DATA(o), sizeof(u32));
|
|
|
|
u32 = ntohl(u32);
|
|
|
|
if (u32 >= 60 && u32 <= 86400) {
|
|
|
|
syslog(LOG_DEBUG, "%s: SOL_MAX_RT %d -> %d",
|
|
|
|
ifp->name, state->sol_max_rt, u32);
|
|
|
|
state->sol_max_rt = u32;
|
|
|
|
} else
|
|
|
|
syslog(LOG_ERR, "%s: invalid SOL_MAX_RT %d",
|
|
|
|
ifp->name, u32);
|
|
|
|
}
|
|
|
|
o = dhcp6_getmoption(D6_OPTION_INF_MAX_RT, r, len);
|
|
|
|
if (o && ntohs(o->len) >= sizeof(u32)) {
|
|
|
|
memcpy(&u32, D6_COPTION_DATA(o), sizeof(u32));
|
|
|
|
u32 = ntohl(u32);
|
|
|
|
if (u32 >= 60 && u32 <= 86400) {
|
|
|
|
syslog(LOG_DEBUG, "%s: INF_MAX_RT %d -> %d",
|
|
|
|
ifp->name, state->inf_max_rt, u32);
|
|
|
|
state->inf_max_rt = u32;
|
|
|
|
} else
|
|
|
|
syslog(LOG_ERR, "%s: invalid INF_MAX_RT %d",
|
|
|
|
ifp->name, u32);
|
|
|
|
}
|
2014-02-12 08:39:46 +08:00
|
|
|
if (dhcp6_validatelease(ifp, r, len, ctx->sfrom) == -1)
|
2012-11-07 07:40:15 +08:00
|
|
|
return;
|
|
|
|
break;
|
2014-01-31 22:25:18 +08:00
|
|
|
case DHCP6_RECONFIGURE:
|
|
|
|
if (auth == NULL) {
|
|
|
|
syslog(LOG_ERR,
|
|
|
|
"%s: unauthenticated Force Renew from %s",
|
2014-02-12 08:39:46 +08:00
|
|
|
ifp->name, ctx->sfrom);
|
2014-01-31 22:25:18 +08:00
|
|
|
return;
|
|
|
|
}
|
2014-02-12 08:39:46 +08:00
|
|
|
syslog(LOG_INFO, "%s: Force Renew from %s",
|
|
|
|
ifp->name, ctx->sfrom);
|
2014-01-31 22:25:18 +08:00
|
|
|
o = dhcp6_getmoption(D6_OPTION_RECONF_MSG, r, len);
|
|
|
|
if (o == NULL) {
|
|
|
|
syslog(LOG_ERR,
|
|
|
|
"%s: missing Reconfigure Message option",
|
|
|
|
ifp->name);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (ntohs(o->len) != 1) {
|
|
|
|
syslog(LOG_ERR,
|
|
|
|
"%s: missing Reconfigure Message type",
|
|
|
|
ifp->name);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
switch(*D6_COPTION_DATA(o)) {
|
|
|
|
case DHCP6_RENEW:
|
|
|
|
if (state->state != DH6S_BOUND) {
|
|
|
|
syslog(LOG_ERR,
|
|
|
|
"%s: not bound, ignoring Force Renew",
|
|
|
|
ifp->name);
|
|
|
|
return;
|
|
|
|
}
|
2014-02-12 08:39:46 +08:00
|
|
|
eloop_timeout_delete(ifp->ctx->eloop,
|
|
|
|
dhcp6_startrenew, ifp);
|
2014-01-31 22:25:18 +08:00
|
|
|
dhcp6_startrenew(ifp);
|
|
|
|
break;
|
|
|
|
case DHCP6_INFORMATION_REQ:
|
|
|
|
if (state->state != DH6S_INFORMED) {
|
|
|
|
syslog(LOG_ERR,
|
|
|
|
"%s: not informed, ignoring Force Renew",
|
|
|
|
ifp->name);
|
|
|
|
return;
|
|
|
|
}
|
2014-02-12 08:39:46 +08:00
|
|
|
eloop_timeout_delete(ifp->ctx->eloop,
|
|
|
|
dhcp6_sendinform, ifp);
|
2014-01-31 22:25:18 +08:00
|
|
|
dhcp6_startinform(ifp);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
syslog(LOG_ERR,
|
|
|
|
"%s: unsupported Reconfigure Message type",
|
|
|
|
ifp->name);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
break;
|
2012-11-07 07:40:15 +08:00
|
|
|
default:
|
2012-10-12 18:31:51 +08:00
|
|
|
syslog(LOG_ERR, "%s: invalid DHCP6 type %s (%d)",
|
|
|
|
ifp->name, op, r->type);
|
|
|
|
return;
|
|
|
|
}
|
2012-11-07 07:40:15 +08:00
|
|
|
if (op == NULL) {
|
|
|
|
syslog(LOG_WARNING, "%s: invalid state for DHCP6 type %s (%d)",
|
|
|
|
ifp->name, op, r->type);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (state->recv_len < (size_t)len) {
|
|
|
|
free(state->recv);
|
|
|
|
state->recv = malloc(len);
|
|
|
|
if (state->recv == NULL) {
|
|
|
|
syslog(LOG_ERR, "%s: malloc recv: %m", ifp->name);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
memcpy(state->recv, r, len);
|
|
|
|
state->recv_len = len;
|
|
|
|
|
|
|
|
switch(r->type) {
|
|
|
|
case DHCP6_ADVERTISE:
|
2013-06-06 07:57:03 +08:00
|
|
|
if (state->state == DH6S_REQUEST) /* rapid commit */
|
|
|
|
break;
|
2012-11-07 07:40:15 +08:00
|
|
|
ap = TAILQ_FIRST(&state->addrs);
|
|
|
|
syslog(LOG_INFO, "%s: ADV %s from %s",
|
2014-02-12 08:39:46 +08:00
|
|
|
ifp->name, ap->saddr, ctx->sfrom);
|
|
|
|
if (ifp->ctx->options & DHCPCD_TEST)
|
2013-12-10 20:28:38 +08:00
|
|
|
break;
|
2012-11-07 07:40:15 +08:00
|
|
|
dhcp6_startrequest(ifp);
|
|
|
|
return;
|
|
|
|
}
|
2012-10-12 18:31:51 +08:00
|
|
|
|
2012-11-07 07:40:15 +08:00
|
|
|
recv:
|
2013-06-02 15:12:26 +08:00
|
|
|
has_new = 0;
|
2013-05-24 05:50:34 +08:00
|
|
|
TAILQ_FOREACH(ap, &state->addrs, next) {
|
2013-05-31 21:01:54 +08:00
|
|
|
if (ap->flags & IPV6_AF_NEW) {
|
2013-06-02 15:12:26 +08:00
|
|
|
has_new = 1;
|
2013-05-24 05:50:34 +08:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2013-06-02 15:12:26 +08:00
|
|
|
syslog(has_new ? LOG_INFO : LOG_DEBUG,
|
2014-02-12 08:39:46 +08:00
|
|
|
"%s: %s received from %s", ifp->name, op, ctx->sfrom);
|
2012-11-07 07:40:15 +08:00
|
|
|
|
2013-05-15 18:27:36 +08:00
|
|
|
state->reason = NULL;
|
2014-02-12 08:39:46 +08:00
|
|
|
eloop_timeout_delete(ifp->ctx->eloop, NULL, ifp);
|
2012-10-12 18:31:51 +08:00
|
|
|
switch(state->state) {
|
|
|
|
case DH6S_INFORM:
|
2012-11-07 07:40:15 +08:00
|
|
|
state->rebind = 0;
|
2013-05-31 21:40:46 +08:00
|
|
|
state->expire = ND6_INFINITE_LIFETIME;
|
|
|
|
state->lowpl = ND6_INFINITE_LIFETIME;
|
2013-05-15 18:27:36 +08:00
|
|
|
state->reason = "INFORM6";
|
2012-10-12 18:31:51 +08:00
|
|
|
break;
|
2012-11-07 07:40:15 +08:00
|
|
|
case DH6S_REQUEST:
|
2013-05-15 18:27:36 +08:00
|
|
|
if (state->reason == NULL)
|
|
|
|
state->reason = "BOUND6";
|
2012-11-07 07:40:15 +08:00
|
|
|
/* FALLTHROUGH */
|
|
|
|
case DH6S_RENEW:
|
2013-05-15 18:27:36 +08:00
|
|
|
if (state->reason == NULL)
|
|
|
|
state->reason = "RENEW6";
|
2012-11-07 07:40:15 +08:00
|
|
|
/* FALLTHROUGH */
|
|
|
|
case DH6S_REBIND:
|
2013-05-15 18:27:36 +08:00
|
|
|
if (state->reason == NULL)
|
|
|
|
state->reason = "REBIND6";
|
2013-11-13 01:30:43 +08:00
|
|
|
/* FALLTHROUGH */
|
2013-04-01 20:15:47 +08:00
|
|
|
case DH6S_CONFIRM:
|
2013-05-15 18:27:36 +08:00
|
|
|
if (state->reason == NULL)
|
|
|
|
state->reason = "REBOOT6";
|
2012-11-07 07:40:15 +08:00
|
|
|
if (state->renew == 0) {
|
2013-05-31 21:40:46 +08:00
|
|
|
if (state->expire == ND6_INFINITE_LIFETIME)
|
|
|
|
state->renew = ND6_INFINITE_LIFETIME;
|
2012-11-07 07:40:15 +08:00
|
|
|
else
|
|
|
|
state->renew = state->lowpl * 0.5;
|
|
|
|
}
|
|
|
|
if (state->rebind == 0) {
|
2013-05-31 21:40:46 +08:00
|
|
|
if (state->expire == ND6_INFINITE_LIFETIME)
|
|
|
|
state->rebind = ND6_INFINITE_LIFETIME;
|
2012-11-07 07:40:15 +08:00
|
|
|
else
|
|
|
|
state->rebind = state->lowpl * 0.8;
|
|
|
|
}
|
|
|
|
break;
|
2012-10-12 18:31:51 +08:00
|
|
|
default:
|
2013-05-15 18:27:36 +08:00
|
|
|
state->reason = "UNKNOWN6";
|
2012-10-12 18:31:51 +08:00
|
|
|
break;
|
|
|
|
}
|
2012-11-07 07:40:15 +08:00
|
|
|
|
2013-04-01 20:15:47 +08:00
|
|
|
if (state->state != DH6S_CONFIRM) {
|
2012-11-07 07:40:15 +08:00
|
|
|
free(state->old);
|
|
|
|
state->old = state->new;
|
|
|
|
state->old_len = state->new_len;
|
|
|
|
state->new = state->recv;
|
|
|
|
state->new_len = state->recv_len;
|
|
|
|
state->recv = NULL;
|
|
|
|
state->recv_len = 0;
|
|
|
|
}
|
|
|
|
|
2014-02-12 08:39:46 +08:00
|
|
|
if (ifp->ctx->options & DHCPCD_TEST)
|
2013-05-15 18:27:36 +08:00
|
|
|
script_runreason(ifp, "TEST");
|
|
|
|
else {
|
2013-06-12 23:01:59 +08:00
|
|
|
if (state->state == DH6S_INFORM)
|
|
|
|
state->state = DH6S_INFORMED;
|
|
|
|
else
|
2013-06-12 18:58:28 +08:00
|
|
|
state->state = DH6S_BOUND;
|
2013-05-31 21:40:46 +08:00
|
|
|
if (state->renew && state->renew != ND6_INFINITE_LIFETIME)
|
2014-02-12 08:39:46 +08:00
|
|
|
eloop_timeout_add_sec(ifp->ctx->eloop, state->renew,
|
2013-11-13 01:30:43 +08:00
|
|
|
state->state == DH6S_INFORMED ?
|
|
|
|
dhcp6_startinform : dhcp6_startrenew, ifp);
|
2013-05-31 21:40:46 +08:00
|
|
|
if (state->rebind && state->rebind != ND6_INFINITE_LIFETIME)
|
2014-02-12 08:39:46 +08:00
|
|
|
eloop_timeout_add_sec(ifp->ctx->eloop, state->rebind,
|
2012-11-13 19:25:51 +08:00
|
|
|
dhcp6_startrebind, ifp);
|
2013-05-31 21:40:46 +08:00
|
|
|
if (state->expire && state->expire != ND6_INFINITE_LIFETIME)
|
2014-02-12 08:39:46 +08:00
|
|
|
eloop_timeout_add_sec(ifp->ctx->eloop, state->expire,
|
2012-11-13 19:25:51 +08:00
|
|
|
dhcp6_startexpire, ifp);
|
2013-04-01 20:15:47 +08:00
|
|
|
if (ifp->options->ia_type == D6_OPTION_IA_PD)
|
|
|
|
dhcp6_delegate_prefix(ifp);
|
2014-03-03 21:56:49 +08:00
|
|
|
|
2014-03-07 03:11:55 +08:00
|
|
|
ipv6_addaddrs(&state->addrs);
|
2013-11-13 01:30:43 +08:00
|
|
|
if (state->state == DH6S_INFORMED)
|
|
|
|
syslog(has_new ? LOG_INFO : LOG_DEBUG,
|
|
|
|
"%s: refresh in %"PRIu32" seconds",
|
|
|
|
ifp->name, state->renew);
|
|
|
|
else if (state->renew || state->rebind)
|
2013-06-02 15:12:26 +08:00
|
|
|
syslog(has_new ? LOG_INFO : LOG_DEBUG,
|
2013-05-31 22:14:52 +08:00
|
|
|
"%s: renew in %"PRIu32" seconds,"
|
|
|
|
" rebind in %"PRIu32" seconds",
|
2012-11-07 07:40:15 +08:00
|
|
|
ifp->name, state->renew, state->rebind);
|
2014-02-12 08:39:46 +08:00
|
|
|
ipv6_buildroutes(ifp->ctx);
|
2012-11-07 07:40:15 +08:00
|
|
|
dhcp6_writelease(ifp);
|
2013-05-15 18:27:36 +08:00
|
|
|
|
|
|
|
len = 1;
|
|
|
|
/* If all addresses have completed DAD run the script */
|
|
|
|
TAILQ_FOREACH(ap, &state->addrs, next) {
|
2013-06-09 15:31:08 +08:00
|
|
|
if (ap->flags & IPV6_AF_ONLINK) {
|
2014-03-03 21:56:49 +08:00
|
|
|
if (!(ap->flags & IPV6_AF_DADCOMPLETED) &&
|
|
|
|
ipv6_findaddr(ap->iface, &ap->addr))
|
|
|
|
ap->flags |= IPV6_AF_DADCOMPLETED;
|
2013-06-09 15:31:08 +08:00
|
|
|
if ((ap->flags & IPV6_AF_DADCOMPLETED) == 0) {
|
|
|
|
len = 0;
|
|
|
|
break;
|
|
|
|
}
|
2013-05-15 18:27:36 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
if (len) {
|
|
|
|
script_runreason(ifp, state->reason);
|
2014-02-12 08:39:46 +08:00
|
|
|
daemonise(ifp->ctx);
|
2013-05-15 18:27:36 +08:00
|
|
|
} else
|
2013-05-24 05:50:34 +08:00
|
|
|
syslog(LOG_DEBUG,
|
|
|
|
"%s: waiting for DHCPv6 DAD to complete",
|
2013-05-15 18:27:36 +08:00
|
|
|
ifp->name);
|
2012-11-07 07:40:15 +08:00
|
|
|
}
|
|
|
|
|
2014-02-12 08:39:46 +08:00
|
|
|
if (ifp->ctx->options & DHCPCD_TEST ||
|
2013-02-03 18:35:59 +08:00
|
|
|
(ifp->options->options & DHCPCD_INFORM &&
|
2014-02-12 08:39:46 +08:00
|
|
|
!(ifp->ctx->options & DHCPCD_MASTER)))
|
2012-10-12 18:31:51 +08:00
|
|
|
{
|
2014-02-12 08:39:46 +08:00
|
|
|
eloop_exit(ifp->ctx->eloop, EXIT_SUCCESS);
|
2012-10-12 18:31:51 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
2014-02-12 08:39:46 +08:00
|
|
|
dhcp6_open(struct dhcpcd_ctx *dctx)
|
2012-10-12 18:31:51 +08:00
|
|
|
{
|
2014-02-12 08:39:46 +08:00
|
|
|
struct ipv6_ctx *ctx;
|
2012-10-12 18:31:51 +08:00
|
|
|
struct sockaddr_in6 sa;
|
|
|
|
int n;
|
|
|
|
|
|
|
|
memset(&sa, 0, sizeof(sa));
|
|
|
|
sa.sin6_family = AF_INET6;
|
|
|
|
sa.sin6_port = htons(DHCP6_CLIENT_PORT);
|
|
|
|
#ifdef BSD
|
|
|
|
sa.sin6_len = sizeof(sa);
|
|
|
|
#endif
|
|
|
|
|
2014-02-12 08:39:46 +08:00
|
|
|
ctx = dctx->ipv6;
|
2014-02-13 20:58:58 +08:00
|
|
|
#ifdef SOCK_CLOEXEC
|
2014-02-12 08:39:46 +08:00
|
|
|
ctx->dhcp_fd = socket(PF_INET6,
|
|
|
|
SOCK_DGRAM | SOCK_CLOEXEC | SOCK_NONBLOCK,
|
2014-02-08 01:32:08 +08:00
|
|
|
IPPROTO_UDP);
|
2014-02-12 08:39:46 +08:00
|
|
|
if (ctx->dhcp_fd == -1)
|
2012-10-12 18:31:51 +08:00
|
|
|
return -1;
|
2014-02-13 20:58:58 +08:00
|
|
|
#else
|
|
|
|
if ((ctx->dhcp_fd = socket(PF_INET6, SOCK_DGRAM, IPPROTO_UDP)) == -1)
|
|
|
|
return -1;
|
|
|
|
if ((n = fcntl(ctx->dhcp_fd, F_GETFD, 0)) == -1 ||
|
|
|
|
fcntl(ctx->dhcp_fd, F_SETFD, n | FD_CLOEXEC) == -1)
|
|
|
|
{
|
|
|
|
close(ctx->dhcp_fd);
|
|
|
|
ctx->dhcp_fd = -1;
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
if ((n = fcntl(ctx->dhcp_fd, F_GETFL, 0)) == -1 ||
|
|
|
|
fcntl(ctx->dhcp_fd, F_SETFL, n | O_NONBLOCK) == -1)
|
|
|
|
{
|
|
|
|
close(ctx->dhcp_fd);
|
|
|
|
ctx->dhcp_fd = -1;
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
#endif
|
2012-10-12 18:31:51 +08:00
|
|
|
|
|
|
|
n = 1;
|
2014-02-12 08:39:46 +08:00
|
|
|
if (setsockopt(ctx->dhcp_fd, SOL_SOCKET, SO_REUSEADDR,
|
2012-10-12 18:31:51 +08:00
|
|
|
&n, sizeof(n)) == -1)
|
|
|
|
goto errexit;
|
|
|
|
|
|
|
|
n = 1;
|
2014-02-12 08:39:46 +08:00
|
|
|
if (setsockopt(ctx->dhcp_fd, SOL_SOCKET, SO_BROADCAST,
|
2012-10-12 18:31:51 +08:00
|
|
|
&n, sizeof(n)) == -1)
|
|
|
|
goto errexit;
|
|
|
|
|
|
|
|
#ifdef SO_REUSEPORT
|
|
|
|
n = 1;
|
2014-02-12 08:39:46 +08:00
|
|
|
if (setsockopt(ctx->dhcp_fd, SOL_SOCKET, SO_REUSEPORT,
|
2012-10-12 18:31:51 +08:00
|
|
|
&n, sizeof(n)) == -1)
|
|
|
|
goto errexit;
|
|
|
|
#endif
|
|
|
|
|
2014-02-12 08:39:46 +08:00
|
|
|
if (bind(ctx->dhcp_fd, (struct sockaddr *)&sa, sizeof(sa)) == -1)
|
2012-10-12 18:31:51 +08:00
|
|
|
goto errexit;
|
|
|
|
|
|
|
|
n = 1;
|
2014-02-12 08:39:46 +08:00
|
|
|
if (setsockopt(ctx->dhcp_fd, IPPROTO_IPV6, IPV6_RECVPKTINFO,
|
2012-10-12 18:31:51 +08:00
|
|
|
&n, sizeof(n)) == -1)
|
|
|
|
goto errexit;
|
|
|
|
|
2014-02-12 08:39:46 +08:00
|
|
|
n = 1;
|
|
|
|
if (setsockopt(ctx->dhcp_fd, IPPROTO_IPV6, IPV6_RECVHOPLIMIT,
|
|
|
|
&n, sizeof(n)) == -1)
|
|
|
|
goto errexit;
|
|
|
|
|
|
|
|
eloop_event_add(dctx->eloop, ctx->dhcp_fd, dhcp6_handledata, dctx);
|
2012-10-12 18:31:51 +08:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
errexit:
|
2014-02-12 08:39:46 +08:00
|
|
|
close(ctx->dhcp_fd);
|
|
|
|
ctx->dhcp_fd = -1;
|
2012-10-12 18:31:51 +08:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2013-05-31 06:39:17 +08:00
|
|
|
static void
|
|
|
|
dhcp6_start1(void *arg)
|
|
|
|
{
|
|
|
|
struct interface *ifp = arg;
|
2013-06-06 04:13:14 +08:00
|
|
|
struct if_options *ifo = ifp->options;
|
2013-05-31 06:39:17 +08:00
|
|
|
struct dhcp6_state *state;
|
2013-06-06 04:13:14 +08:00
|
|
|
size_t i;
|
|
|
|
const struct dhcp_compat *dhc;
|
2013-05-31 06:39:17 +08:00
|
|
|
|
|
|
|
state = D6_STATE(ifp);
|
2013-06-06 04:13:14 +08:00
|
|
|
/* Match any DHCPv4 opton to DHCPv6 options if given for easy
|
|
|
|
* configuration */
|
|
|
|
for (i = 0; i < sizeof(ifo->requestmask6); i++) {
|
|
|
|
if (ifo->requestmask6[i] != '\0')
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (i == sizeof(ifo->requestmask6)) {
|
|
|
|
for (dhc = dhcp_compats; dhc->dhcp_opt; dhc++) {
|
|
|
|
if (has_option_mask(ifo->requestmask, dhc->dhcp_opt))
|
|
|
|
add_option_mask(ifo->requestmask6,
|
|
|
|
dhc->dhcp6_opt);
|
|
|
|
}
|
2013-07-23 03:05:54 +08:00
|
|
|
if (ifo->fqdn != FQDN_DISABLE ||
|
|
|
|
ifo->options & DHCPCD_HOSTNAME)
|
2013-06-06 04:13:14 +08:00
|
|
|
add_option_mask(ifo->requestmask6, D6_OPTION_FQDN);
|
|
|
|
}
|
|
|
|
|
2013-11-13 01:30:43 +08:00
|
|
|
if (state->state == DH6S_INFORM) {
|
|
|
|
add_option_mask(ifo->requestmask6, D6_OPTION_INFO_REFRESH_TIME);
|
2013-05-31 06:39:17 +08:00
|
|
|
dhcp6_startinform(ifp);
|
2013-11-13 01:30:43 +08:00
|
|
|
} else {
|
|
|
|
del_option_mask(ifo->requestmask6, D6_OPTION_INFO_REFRESH_TIME);
|
2013-05-31 06:39:17 +08:00
|
|
|
dhcp6_startinit(ifp);
|
2013-11-13 01:30:43 +08:00
|
|
|
}
|
2013-05-31 06:39:17 +08:00
|
|
|
}
|
|
|
|
|
2012-10-12 18:31:51 +08:00
|
|
|
int
|
2013-05-31 23:01:27 +08:00
|
|
|
dhcp6_start(struct interface *ifp, enum DH6S init_state)
|
2012-10-12 18:31:51 +08:00
|
|
|
{
|
|
|
|
struct dhcp6_state *state;
|
|
|
|
|
|
|
|
state = D6_STATE(ifp);
|
|
|
|
if (state) {
|
2013-06-02 05:30:18 +08:00
|
|
|
if (state->state == DH6S_DELEGATED) {
|
|
|
|
dhcp6_find_delegates(ifp);
|
|
|
|
return 0;
|
|
|
|
}
|
2013-06-12 23:01:59 +08:00
|
|
|
if (state->state == DH6S_INFORMED &&
|
|
|
|
init_state == DH6S_INFORM)
|
|
|
|
{
|
2013-06-12 18:58:28 +08:00
|
|
|
dhcp6_startinform(ifp);
|
|
|
|
return 0;
|
|
|
|
}
|
2012-10-12 18:31:51 +08:00
|
|
|
/* We're already running DHCP6 */
|
|
|
|
/* XXX: What if the managed flag changes? */
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2014-01-29 22:05:58 +08:00
|
|
|
if (!(ifp->options->options & DHCPCD_DHCP6))
|
|
|
|
return 0;
|
|
|
|
|
2014-02-12 08:39:46 +08:00
|
|
|
if (ifp->ctx->ipv6->dhcp_fd == -1 && dhcp6_open(ifp->ctx) == -1)
|
2012-10-12 18:31:51 +08:00
|
|
|
return -1;
|
|
|
|
|
|
|
|
ifp->if_data[IF_DATA_DHCP6] = calloc(1, sizeof(*state));
|
|
|
|
state = D6_STATE(ifp);
|
|
|
|
if (state == NULL)
|
|
|
|
return -1;
|
|
|
|
|
Add support for the following RFCs:
DHCP SLP Directory Agent, RFC2610
DHCP Name Service Search, RFC2937
DHCP PANA Authentication Agent, RFC5192
DHCP Lost Server, RFC5223
DHCP CAPWAP, RFC5417
DHCP Mobility Services, RFC5678
DHCP SIP UA, RFC6011
DHCP ANDSF, RFC6153
DHCP RDNSS Selection for MIF Nodes, RFC6731
DHCP TFTP Server Address, RFC5859
DHCP PXELINUX, RFC5071
DHCP Access Network Domain Name, RFC5986
DHCP Virtual Subnet Selection, RFC6607
DHCP Relay Agent Remote-ID, RFC4649
DHCP Relay Agent Subscriber-ID, RFC4580
DHCPv6 Relay-ID, RFC5460
DHCPv6 LIS Discovery, RFC5986
DHCPv6 SIP UA, RFC6011
DHCPv6 Network Boot, RFC5970
DHCPv6 Home Info Discovery in MIPv6, RFC6610
DHCPv6 RDNSS Selection for MIF Nodes, RFC6731
DHCPv6 Kerberos, RFC6784
DHCPv6 Relay-Triggered Reconfiguraion, RFC6977
DHCPv6 SOL_MAX_RT, RFC7083
2014-02-03 18:28:30 +08:00
|
|
|
state->sol_max_rt = SOL_MAX_RT;
|
|
|
|
state->inf_max_rt = INF_MAX_RT;
|
|
|
|
|
2012-11-07 07:40:15 +08:00
|
|
|
TAILQ_INIT(&state->addrs);
|
2013-05-15 18:27:36 +08:00
|
|
|
if (dhcp6_find_delegates(ifp))
|
2013-04-01 20:15:47 +08:00
|
|
|
return 0;
|
|
|
|
|
2013-05-31 06:39:17 +08:00
|
|
|
state->state = init_state;
|
2012-11-07 07:40:15 +08:00
|
|
|
snprintf(state->leasefile, sizeof(state->leasefile),
|
|
|
|
LEASEFILE6, ifp->name);
|
|
|
|
|
2013-05-31 06:39:17 +08:00
|
|
|
if (ipv6_linklocal(ifp) == NULL) {
|
|
|
|
syslog(LOG_DEBUG,
|
|
|
|
"%s: delaying DHCPv6 soliciation for LL address",
|
|
|
|
ifp->name);
|
|
|
|
ipv6_addlinklocalcallback(ifp, dhcp6_start1, ifp);
|
|
|
|
return 0;
|
|
|
|
}
|
2012-10-12 18:31:51 +08:00
|
|
|
|
2013-05-31 06:39:17 +08:00
|
|
|
dhcp6_start1(ifp);
|
|
|
|
return 0;
|
2012-10-12 18:31:51 +08:00
|
|
|
}
|
|
|
|
|
2013-06-07 20:44:19 +08:00
|
|
|
void
|
|
|
|
dhcp6_reboot(struct interface *ifp)
|
|
|
|
{
|
|
|
|
struct dhcp6_state *state;
|
|
|
|
|
|
|
|
state = D6_STATE(ifp);
|
2013-06-12 23:01:59 +08:00
|
|
|
if (state) {
|
|
|
|
switch (state->state) {
|
|
|
|
case DH6S_BOUND:
|
|
|
|
dhcp6_startrebind(ifp);
|
|
|
|
break;
|
|
|
|
case DH6S_INFORMED:
|
|
|
|
dhcp6_startinform(ifp);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
dhcp6_startdiscover(ifp);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2013-06-07 20:44:19 +08:00
|
|
|
}
|
|
|
|
|
2012-10-12 18:31:51 +08:00
|
|
|
static void
|
2012-11-12 03:56:01 +08:00
|
|
|
dhcp6_freedrop(struct interface *ifp, int drop, const char *reason)
|
2012-10-12 18:31:51 +08:00
|
|
|
{
|
|
|
|
struct dhcp6_state *state;
|
2014-02-12 08:39:46 +08:00
|
|
|
struct dhcpcd_ctx *ctx;
|
2014-03-04 03:57:24 +08:00
|
|
|
unsigned long long options;
|
2012-10-12 18:31:51 +08:00
|
|
|
|
2014-02-12 08:39:46 +08:00
|
|
|
eloop_timeout_delete(ifp->ctx->eloop, NULL, ifp);
|
2013-06-19 06:20:52 +08:00
|
|
|
|
2014-02-28 04:00:39 +08:00
|
|
|
if (ifp->options)
|
|
|
|
options = ifp->options->options;
|
|
|
|
else
|
|
|
|
options = 0;
|
2014-03-04 03:57:24 +08:00
|
|
|
|
2013-06-19 06:20:52 +08:00
|
|
|
/*
|
|
|
|
* As the interface is going away from dhcpcd we need to
|
|
|
|
* remove the delegated addresses, otherwise we lose track
|
|
|
|
* of which interface is delegating as we remeber it by pointer.
|
|
|
|
* So if we need to change this behaviour, we need to change
|
|
|
|
* how we remember which interface delegated.
|
2014-02-01 02:33:11 +08:00
|
|
|
*
|
|
|
|
* XXX The below is no longer true due to the change of the
|
2014-02-28 04:00:39 +08:00
|
|
|
* default IAID, but do PPP links have stable ethernet
|
|
|
|
* addresses?
|
2014-02-01 02:33:11 +08:00
|
|
|
*
|
2013-06-19 06:20:52 +08:00
|
|
|
* To make it more interesting, on some OS's with PPP links
|
|
|
|
* there is no guarantee the delegating interface will have
|
|
|
|
* the same name or index so think very hard before changing
|
|
|
|
* this.
|
|
|
|
*/
|
2014-02-28 04:00:39 +08:00
|
|
|
if (options & (DHCPCD_STOPPING | DHCPCD_RELEASE) &&
|
|
|
|
(options &
|
|
|
|
(DHCPCD_EXITING | DHCPCD_PERSISTENT)) !=
|
2013-08-25 18:22:48 +08:00
|
|
|
(DHCPCD_EXITING | DHCPCD_PERSISTENT))
|
2013-06-19 06:20:52 +08:00
|
|
|
dhcp6_delete_delegates(ifp);
|
|
|
|
|
2012-10-12 18:31:51 +08:00
|
|
|
state = D6_STATE(ifp);
|
|
|
|
if (state) {
|
2014-02-01 02:33:11 +08:00
|
|
|
dhcp_auth_reset(&state->auth);
|
2014-02-28 04:00:39 +08:00
|
|
|
if (options & DHCPCD_RELEASE) {
|
2013-04-05 07:30:14 +08:00
|
|
|
if (ifp->carrier != LINK_DOWN)
|
|
|
|
dhcp6_startrelease(ifp);
|
2013-04-05 05:59:02 +08:00
|
|
|
unlink(state->leasefile);
|
|
|
|
}
|
2013-06-11 05:54:00 +08:00
|
|
|
dhcp6_freedrop_addrs(ifp, drop, NULL);
|
2013-08-25 18:22:48 +08:00
|
|
|
if (drop && state->new &&
|
2014-02-28 04:00:39 +08:00
|
|
|
(options &
|
2013-08-25 18:22:48 +08:00
|
|
|
(DHCPCD_EXITING | DHCPCD_PERSISTENT)) !=
|
|
|
|
(DHCPCD_EXITING | DHCPCD_PERSISTENT))
|
|
|
|
{
|
2012-11-12 03:56:01 +08:00
|
|
|
if (reason == NULL)
|
|
|
|
reason = "STOP6";
|
2013-02-02 22:05:55 +08:00
|
|
|
script_runreason(ifp, reason);
|
2012-11-12 03:56:01 +08:00
|
|
|
}
|
2012-10-12 18:31:51 +08:00
|
|
|
free(state->send);
|
2012-11-07 07:40:15 +08:00
|
|
|
free(state->recv);
|
2012-10-12 18:31:51 +08:00
|
|
|
free(state->new);
|
|
|
|
free(state->old);
|
|
|
|
free(state);
|
|
|
|
ifp->if_data[IF_DATA_DHCP6] = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* If we don't have any more DHCP6 enabled interfaces,
|
2014-02-28 04:00:39 +08:00
|
|
|
* close the global socket and release resources */
|
2014-02-12 08:39:46 +08:00
|
|
|
ctx = ifp->ctx;
|
|
|
|
if (ctx->ifaces) {
|
|
|
|
TAILQ_FOREACH(ifp, ctx->ifaces, next) {
|
2013-02-19 21:37:42 +08:00
|
|
|
if (D6_STATE(ifp))
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2014-02-13 04:51:57 +08:00
|
|
|
if (ifp == NULL && ctx->ipv6) {
|
2014-02-12 08:39:46 +08:00
|
|
|
if (ctx->ipv6->dhcp_fd != -1) {
|
|
|
|
eloop_event_delete(ctx->eloop, ctx->ipv6->dhcp_fd);
|
2014-02-12 18:29:06 +08:00
|
|
|
close(ctx->ipv6->dhcp_fd);
|
2014-02-12 08:39:46 +08:00
|
|
|
ctx->ipv6->dhcp_fd = -1;
|
|
|
|
}
|
2012-10-12 18:31:51 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2012-11-12 03:56:01 +08:00
|
|
|
dhcp6_drop(struct interface *ifp, const char *reason)
|
2012-10-12 18:31:51 +08:00
|
|
|
{
|
|
|
|
|
2012-11-12 03:56:01 +08:00
|
|
|
dhcp6_freedrop(ifp, 1, reason);
|
2012-10-12 18:31:51 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
dhcp6_free(struct interface *ifp)
|
|
|
|
{
|
|
|
|
|
2012-11-12 03:56:01 +08:00
|
|
|
dhcp6_freedrop(ifp, 0, NULL);
|
2012-10-12 18:31:51 +08:00
|
|
|
}
|
|
|
|
|
2013-05-15 18:27:36 +08:00
|
|
|
void
|
2014-02-12 08:39:46 +08:00
|
|
|
dhcp6_handleifa(struct dhcpcd_ctx *ctx, int cmd, const char *ifname,
|
2013-05-16 18:31:21 +08:00
|
|
|
const struct in6_addr *addr, int flags)
|
2013-05-15 18:27:36 +08:00
|
|
|
{
|
|
|
|
struct interface *ifp;
|
|
|
|
struct dhcp6_state *state;
|
|
|
|
|
2014-02-12 08:39:46 +08:00
|
|
|
if (ctx->ifaces == NULL)
|
2013-06-09 15:31:08 +08:00
|
|
|
return;
|
|
|
|
|
2014-02-12 08:39:46 +08:00
|
|
|
TAILQ_FOREACH(ifp, ctx->ifaces, next) {
|
2013-05-15 18:27:36 +08:00
|
|
|
state = D6_STATE(ifp);
|
|
|
|
if (state == NULL || strcmp(ifp->name, ifname))
|
|
|
|
continue;
|
2013-05-16 18:31:21 +08:00
|
|
|
ipv6_handleifa_addrs(cmd, &state->addrs, addr, flags);
|
2013-05-15 18:27:36 +08:00
|
|
|
}
|
2013-11-28 04:21:17 +08:00
|
|
|
|
|
|
|
}
|
|
|
|
|
2012-10-12 18:31:51 +08:00
|
|
|
ssize_t
|
|
|
|
dhcp6_env(char **env, const char *prefix, const struct interface *ifp,
|
2013-12-04 01:55:40 +08:00
|
|
|
const struct dhcp6_message *m, ssize_t len)
|
2012-10-12 18:31:51 +08:00
|
|
|
{
|
2012-11-07 07:40:15 +08:00
|
|
|
const struct dhcp6_state *state;
|
2012-10-12 18:31:51 +08:00
|
|
|
const struct if_options *ifo;
|
2013-12-07 01:47:53 +08:00
|
|
|
struct dhcp_opt *opt, *vo;
|
2012-10-12 18:31:51 +08:00
|
|
|
const struct dhcp6_option *o;
|
2014-03-06 17:12:06 +08:00
|
|
|
size_t i, l, n;
|
2013-12-04 01:55:40 +08:00
|
|
|
uint16_t ol, oc;
|
2013-12-04 18:48:22 +08:00
|
|
|
char *v, *val, *pfx;
|
2012-11-07 07:40:15 +08:00
|
|
|
const struct ipv6_addr *ap;
|
2013-12-07 01:47:53 +08:00
|
|
|
uint32_t en;
|
2014-02-12 08:39:46 +08:00
|
|
|
const struct dhcpcd_ctx *ctx;
|
2012-10-12 18:31:51 +08:00
|
|
|
|
2012-11-07 07:40:15 +08:00
|
|
|
state = D6_CSTATE(ifp);
|
2013-11-28 04:21:17 +08:00
|
|
|
n = 0;
|
2013-02-03 18:35:59 +08:00
|
|
|
ifo = ifp->options;
|
2014-02-12 08:39:46 +08:00
|
|
|
ctx = ifp->ctx;
|
2013-12-03 00:42:09 +08:00
|
|
|
|
2013-12-04 01:55:40 +08:00
|
|
|
/* Zero our indexes */
|
|
|
|
if (env) {
|
2014-02-12 08:39:46 +08:00
|
|
|
for (i = 0, opt = ctx->dhcp6_opts;
|
|
|
|
i < ctx->dhcp6_opts_len;
|
|
|
|
i++, opt++)
|
2013-12-04 01:55:40 +08:00
|
|
|
dhcp_zero_index(opt);
|
|
|
|
for (i = 0, opt = ifp->options->dhcp6_override;
|
|
|
|
i < ifp->options->dhcp6_override_len;
|
|
|
|
i++, opt++)
|
|
|
|
dhcp_zero_index(opt);
|
2014-02-12 08:39:46 +08:00
|
|
|
for (i = 0, opt = ctx->vivso;
|
|
|
|
i < ctx->vivso_len;
|
|
|
|
i++, opt++)
|
2013-12-07 01:47:53 +08:00
|
|
|
dhcp_zero_index(opt);
|
2013-12-04 01:55:40 +08:00
|
|
|
i = strlen(prefix) + strlen("_dhcp6") + 1;
|
|
|
|
pfx = malloc(i);
|
|
|
|
if (pfx == NULL) {
|
|
|
|
syslog(LOG_ERR, "%s: %m", __func__);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
snprintf(pfx, i, "%s_dhcp6", prefix);
|
|
|
|
} else
|
|
|
|
pfx = NULL;
|
2013-12-03 00:42:09 +08:00
|
|
|
|
2013-12-04 01:55:40 +08:00
|
|
|
/* Unlike DHCP, DHCPv6 options *may* occur more than once.
|
|
|
|
* There is also no provision for option concatenation unlike DHCP. */
|
|
|
|
for (o = D6_CFIRST_OPTION(m);
|
|
|
|
len > (ssize_t)sizeof(*o);
|
|
|
|
o = D6_CNEXT_OPTION(o))
|
|
|
|
{
|
2012-10-12 18:31:51 +08:00
|
|
|
ol = ntohs(o->len);
|
2013-12-04 01:55:40 +08:00
|
|
|
len -= sizeof(*o) + ol;
|
|
|
|
if (len < 0) {
|
|
|
|
errno = EINVAL;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
oc = ntohs(o->code);
|
|
|
|
if (has_option_mask(ifo->nomask6, oc))
|
|
|
|
continue;
|
|
|
|
for (i = 0, opt = ifo->dhcp6_override;
|
|
|
|
i < ifo->dhcp6_override_len;
|
|
|
|
i++, opt++)
|
|
|
|
if (opt->option == oc)
|
|
|
|
break;
|
2013-12-07 01:47:53 +08:00
|
|
|
if (i == ifo->dhcp6_override_len &&
|
|
|
|
oc == D6_OPTION_VENDOR_OPTS &&
|
|
|
|
ol > sizeof(en))
|
|
|
|
{
|
|
|
|
memcpy(&en, D6_COPTION_DATA(o), sizeof(en));
|
|
|
|
en = ntohl(en);
|
|
|
|
vo = vivso_find(en, ifp);
|
|
|
|
} else
|
|
|
|
vo = NULL;
|
2013-12-04 18:48:22 +08:00
|
|
|
if (i == ifo->dhcp6_override_len) {
|
2014-02-12 08:39:46 +08:00
|
|
|
for (i = 0, opt = ctx->dhcp6_opts;
|
|
|
|
i < ctx->dhcp6_opts_len;
|
2013-12-04 01:55:40 +08:00
|
|
|
i++, opt++)
|
|
|
|
if (opt->option == oc)
|
|
|
|
break;
|
2014-02-12 08:39:46 +08:00
|
|
|
if (i == ctx->dhcp6_opts_len)
|
2013-12-04 18:48:22 +08:00
|
|
|
opt = NULL;
|
2013-12-04 01:55:40 +08:00
|
|
|
}
|
|
|
|
if (opt) {
|
2014-02-12 08:39:46 +08:00
|
|
|
n += dhcp_envoption(ifp->ctx,
|
|
|
|
env == NULL ? NULL : &env[n],
|
2013-12-04 01:55:40 +08:00
|
|
|
pfx, ifp->name,
|
|
|
|
opt, dhcp6_getoption, D6_COPTION_DATA(o), ol);
|
|
|
|
}
|
2013-12-07 01:47:53 +08:00
|
|
|
if (vo) {
|
2014-02-12 08:39:46 +08:00
|
|
|
n += dhcp_envoption(ifp->ctx,
|
|
|
|
env == NULL ? NULL : &env[n],
|
2013-12-07 01:47:53 +08:00
|
|
|
pfx, ifp->name,
|
|
|
|
vo, dhcp6_getoption,
|
|
|
|
D6_COPTION_DATA(o) + sizeof(en),
|
|
|
|
ol - sizeof(en));
|
|
|
|
}
|
2012-10-12 18:31:51 +08:00
|
|
|
}
|
2013-12-04 01:55:40 +08:00
|
|
|
free(pfx);
|
2012-10-12 18:31:51 +08:00
|
|
|
|
2013-12-04 01:55:40 +08:00
|
|
|
/* It is tempting to remove this section.
|
|
|
|
* However, we need it at least for Delegated Prefixes
|
|
|
|
* (they don't have a DHCPv6 message to parse to get the addressses)
|
|
|
|
* and it's easier for shell scripts to see which addresses have
|
|
|
|
* been added */
|
2012-11-07 07:40:15 +08:00
|
|
|
if (TAILQ_FIRST(&state->addrs)) {
|
2013-12-04 18:48:22 +08:00
|
|
|
if (env) {
|
2013-04-01 20:15:47 +08:00
|
|
|
if (ifo->ia_type == D6_OPTION_IA_PD) {
|
2014-03-06 17:12:06 +08:00
|
|
|
l = strlen(prefix) +
|
2013-04-01 20:15:47 +08:00
|
|
|
strlen("_dhcp6_prefix=");
|
|
|
|
TAILQ_FOREACH(ap, &state->addrs, next) {
|
2014-03-06 17:12:06 +08:00
|
|
|
l += strlen(ap->saddr) + 1;
|
2013-04-01 20:15:47 +08:00
|
|
|
}
|
2014-03-06 17:12:06 +08:00
|
|
|
v = val = env[n] = malloc(l);
|
2013-04-01 20:15:47 +08:00
|
|
|
if (v == NULL) {
|
|
|
|
syslog(LOG_ERR, "%s: %m", __func__);
|
|
|
|
return -1;
|
|
|
|
}
|
2014-03-07 03:30:30 +08:00
|
|
|
i = snprintf(v, l, "%s_dhcp6_prefix=",
|
2014-03-06 17:12:06 +08:00
|
|
|
prefix);
|
|
|
|
v += i;
|
|
|
|
l -= i;
|
2013-04-01 20:15:47 +08:00
|
|
|
TAILQ_FOREACH(ap, &state->addrs, next) {
|
2014-03-06 17:12:06 +08:00
|
|
|
i = strlen(ap->saddr);
|
|
|
|
strlcpy(v, ap->saddr, l);
|
|
|
|
v += i;
|
|
|
|
l -= i;
|
2013-04-01 20:15:47 +08:00
|
|
|
*v++ = ' ';
|
|
|
|
}
|
|
|
|
*--v = '\0';
|
|
|
|
} else {
|
2014-03-06 17:12:06 +08:00
|
|
|
l = strlen(prefix) +
|
2013-04-01 20:15:47 +08:00
|
|
|
strlen("_dhcp6_ip_address=");
|
|
|
|
TAILQ_FOREACH(ap, &state->addrs, next) {
|
2014-03-06 17:12:06 +08:00
|
|
|
l += strlen(ap->saddr) + 1;
|
2013-04-01 20:15:47 +08:00
|
|
|
}
|
2014-03-06 17:12:06 +08:00
|
|
|
v = val = env[n] = malloc(l);
|
2013-04-01 20:15:47 +08:00
|
|
|
if (v == NULL) {
|
|
|
|
syslog(LOG_ERR, "%s: %m", __func__);
|
|
|
|
return -1;
|
|
|
|
}
|
2014-03-07 03:30:30 +08:00
|
|
|
i = snprintf(v, l, "%s_dhcp6_ip_address=",
|
2014-03-06 17:12:06 +08:00
|
|
|
prefix);
|
2014-03-07 03:30:30 +08:00
|
|
|
v += i;
|
|
|
|
l -= i;
|
2013-04-01 20:15:47 +08:00
|
|
|
TAILQ_FOREACH(ap, &state->addrs, next) {
|
2014-03-06 17:12:06 +08:00
|
|
|
i = strlen(ap->saddr);
|
|
|
|
strlcpy(v, ap->saddr, l);
|
|
|
|
v += i;
|
|
|
|
l -= i;
|
2013-04-01 20:15:47 +08:00
|
|
|
*v++ = ' ';
|
|
|
|
}
|
|
|
|
*--v = '\0';
|
2012-11-07 07:40:15 +08:00
|
|
|
}
|
|
|
|
}
|
2013-12-04 18:48:22 +08:00
|
|
|
n++;
|
2012-11-07 07:40:15 +08:00
|
|
|
}
|
|
|
|
|
2013-11-28 04:21:17 +08:00
|
|
|
return n;
|
2012-10-12 18:31:51 +08:00
|
|
|
}
|