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>
|
|
|
|
|
|
|
|
#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>
|
|
|
|
|
2014-07-19 20:09:43 +08:00
|
|
|
#define ELOOP_QUEUE 4
|
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"
|
2014-04-28 20:02:12 +08:00
|
|
|
#include "if.h"
|
|
|
|
#include "if-options.h"
|
2013-08-20 18:29:43 +08:00
|
|
|
#include "ipv6nd.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
|
|
|
|
|
2014-04-24 17:41:25 +08:00
|
|
|
/* Support older systems with different defines */
|
|
|
|
#if !defined(IPV6_RECVPKTINFO) && defined(IPV6_PKTINFO)
|
|
|
|
#define IPV6_RECVPKTINFO IPV6_PKTINFO
|
|
|
|
#endif
|
|
|
|
|
2012-10-12 18:31:51 +08:00
|
|
|
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" },
|
2014-03-11 01:37:24 +08:00
|
|
|
{ DHCP6_RECONFIGURE, "RECONFIURE6" },
|
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"
|
|
|
|
};
|
|
|
|
|
2014-07-11 18:13:52 +08:00
|
|
|
struct dhcp6_ia_addr {
|
|
|
|
struct in6_addr addr;
|
|
|
|
uint32_t pltime;
|
|
|
|
uint32_t vltime;
|
|
|
|
} __packed;
|
|
|
|
|
|
|
|
struct dhcp6_pd_addr {
|
|
|
|
uint32_t pltime;
|
|
|
|
uint32_t vltime;
|
|
|
|
uint8_t prefix_len;
|
|
|
|
struct in6_addr prefix;
|
|
|
|
} __packed;
|
|
|
|
|
2012-10-12 18:31:51 +08:00
|
|
|
void
|
2014-07-04 07:11:00 +08:00
|
|
|
dhcp6_printoptions(const struct dhcpcd_ctx *ctx,
|
|
|
|
const struct dhcp_opt *opts, size_t opts_len)
|
2012-10-12 18:31:51 +08:00
|
|
|
{
|
2014-07-04 07:11:00 +08:00
|
|
|
size_t i, j;
|
|
|
|
const struct dhcp_opt *opt, *opt2;
|
2014-10-02 02:32:03 +08:00
|
|
|
int cols;
|
2012-10-12 18:31:51 +08:00
|
|
|
|
2014-02-12 08:39:46 +08:00
|
|
|
for (i = 0, opt = ctx->dhcp6_opts;
|
|
|
|
i < ctx->dhcp6_opts_len; i++, opt++)
|
2014-07-04 07:11:00 +08:00
|
|
|
{
|
|
|
|
for (j = 0, opt2 = opts; j < opts_len; j++, opt2++)
|
|
|
|
if (opt2->option == opt->option)
|
|
|
|
break;
|
2014-10-02 02:32:03 +08:00
|
|
|
if (j == opts_len) {
|
|
|
|
cols = printf("%05d %s", opt->option, opt->var);
|
|
|
|
dhcp_print_option_encoding(opt, cols);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
for (i = 0, opt = opts; i < opts_len; i++, opt++) {
|
|
|
|
cols = printf("%05d %s", opt->option, opt->var);
|
|
|
|
dhcp_print_option_encoding(opt, cols);
|
2014-07-04 07:11:00 +08:00
|
|
|
}
|
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;
|
2014-08-06 01:03:04 +08:00
|
|
|
size_t len, i;
|
2012-10-12 18:31:51 +08:00
|
|
|
uint8_t *p;
|
|
|
|
uint16_t u16;
|
|
|
|
uint32_t u32;
|
2014-08-06 01:03:04 +08:00
|
|
|
ssize_t vlen;
|
2013-12-07 01:47:53 +08:00
|
|
|
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));
|
2014-08-06 01:03:04 +08:00
|
|
|
if (vlen == -1)
|
|
|
|
vlen = 0;
|
|
|
|
else
|
|
|
|
len += sizeof(uint16_t) + (size_t)vlen;
|
2013-12-07 01:47:53 +08:00
|
|
|
}
|
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;
|
|
|
|
}
|
2014-08-06 01:03:04 +08:00
|
|
|
} else if (vlen) {
|
2013-12-07 01:47:53 +08:00
|
|
|
u16 = htons(vlen);
|
2012-10-12 18:31:51 +08:00
|
|
|
memcpy(p, &u16, sizeof(u16));
|
|
|
|
p += sizeof(u16);
|
2014-08-06 01:03:04 +08:00
|
|
|
memcpy(p, vendor, (size_t)vlen);
|
2012-10-12 18:31:51 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return len;
|
|
|
|
}
|
|
|
|
|
|
|
|
static const struct dhcp6_option *
|
2014-03-28 06:14:52 +08:00
|
|
|
dhcp6_findoption(unsigned int code, const uint8_t *d, size_t len)
|
2012-10-12 18:31:51 +08:00
|
|
|
{
|
|
|
|
const struct dhcp6_option *o;
|
2014-03-28 06:14:52 +08:00
|
|
|
size_t ol;
|
2012-10-12 18:31:51 +08:00
|
|
|
|
|
|
|
code = htons(code);
|
2012-11-07 07:40:15 +08:00
|
|
|
for (o = (const struct dhcp6_option *)d;
|
2014-07-08 15:56:39 +08:00
|
|
|
len >= sizeof(*o);
|
2012-10-12 18:31:51 +08:00
|
|
|
o = D6_CNEXT_OPTION(o))
|
|
|
|
{
|
2014-03-28 06:14:52 +08:00
|
|
|
ol = sizeof(*o) + ntohs(o->len);
|
|
|
|
if (ol > len) {
|
2012-10-12 18:31:51 +08:00
|
|
|
errno = EINVAL;
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
if (o->code == code)
|
|
|
|
return o;
|
2014-03-28 06:14:52 +08:00
|
|
|
len -= ol;
|
2012-10-12 18:31:51 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
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,
|
2014-03-28 06:14:52 +08:00
|
|
|
size_t *os, unsigned int *code, size_t *len,
|
|
|
|
const uint8_t *od, size_t 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 *
|
2014-03-28 06:14:52 +08:00
|
|
|
dhcp6_getmoption(unsigned int code, const struct dhcp6_message *m, size_t len)
|
2012-11-07 07:40:15 +08:00
|
|
|
{
|
|
|
|
|
2014-07-08 15:56:39 +08:00
|
|
|
if (len < sizeof(*m)) {
|
|
|
|
errno = EINVAL;
|
|
|
|
return NULL;
|
|
|
|
}
|
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
|
2014-03-28 06:14:52 +08:00
|
|
|
dhcp6_updateelapsed(struct interface *ifp, struct dhcp6_message *m, size_t len)
|
2012-10-12 18:31:51 +08:00
|
|
|
{
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2014-07-05 03:44:15 +08:00
|
|
|
static const struct if_sla *
|
|
|
|
dhcp6_findselfsla(struct interface *ifp, const uint8_t *iaid)
|
|
|
|
{
|
|
|
|
size_t i, j;
|
|
|
|
|
|
|
|
for (i = 0; i < ifp->options->ia_len; i++) {
|
|
|
|
if (iaid == NULL ||
|
|
|
|
memcmp(&ifp->options->ia[i].iaid, iaid,
|
|
|
|
sizeof(ifp->options->ia[i].iaid)) == 0)
|
|
|
|
{
|
|
|
|
for (j = 0; j < ifp->options->ia[i].sla_len; j++) {
|
|
|
|
if (strcmp(ifp->options->ia[i].sla[j].ifname,
|
|
|
|
ifp->name) == 0)
|
|
|
|
return &ifp->options->ia[i].sla[j];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2014-10-15 22:23:53 +08:00
|
|
|
|
|
|
|
#ifndef ffs32
|
|
|
|
static int
|
|
|
|
ffs32(uint32_t n)
|
|
|
|
{
|
|
|
|
int v;
|
|
|
|
|
|
|
|
if (!n)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
v = 1;
|
|
|
|
if ((n & 0x0000FFFFU) == 0) {
|
|
|
|
n >>= 16;
|
|
|
|
v += 16;
|
|
|
|
}
|
|
|
|
if ((n & 0x000000FFU) == 0) {
|
|
|
|
n >>= 8;
|
|
|
|
v += 8;
|
|
|
|
}
|
|
|
|
if ((n & 0x0000000FU) == 0) {
|
|
|
|
n >>= 4;
|
|
|
|
v += 4;
|
|
|
|
}
|
|
|
|
if ((n & 0x00000003U) == 0) {
|
|
|
|
n >>= 2;
|
|
|
|
v += 2;
|
|
|
|
}
|
2014-10-16 16:29:29 +08:00
|
|
|
if ((n & 0x00000001U) == 0)
|
2014-10-15 22:23:53 +08:00
|
|
|
v += 1;
|
2014-10-16 16:29:29 +08:00
|
|
|
|
2014-10-15 22:23:53 +08:00
|
|
|
return v;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2014-07-05 03:44:15 +08:00
|
|
|
static int
|
|
|
|
dhcp6_delegateaddr(struct in6_addr *addr, struct interface *ifp,
|
2014-10-15 22:23:53 +08:00
|
|
|
const struct ipv6_addr *prefix, const struct if_sla *sla, struct if_ia *ia)
|
2014-07-05 03:44:15 +08:00
|
|
|
{
|
|
|
|
struct dhcp6_state *state;
|
|
|
|
struct if_sla asla;
|
2014-10-15 22:23:53 +08:00
|
|
|
char sabuf[INET6_ADDRSTRLEN];
|
|
|
|
const char *sa;
|
2014-07-05 03:44:15 +08:00
|
|
|
|
|
|
|
state = D6_STATE(ifp);
|
|
|
|
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 -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
TAILQ_INIT(&state->addrs);
|
|
|
|
state->state = DH6S_DELEGATED;
|
|
|
|
state->reason = "DELEGATED6";
|
|
|
|
}
|
|
|
|
|
|
|
|
if (sla == NULL || sla->sla_set == 0) {
|
|
|
|
asla.sla = ifp->index;
|
2014-10-15 22:23:53 +08:00
|
|
|
asla.prefix_len = 0;
|
|
|
|
sla = &asla;
|
|
|
|
|
|
|
|
if (ia->sla_max == 0) {
|
2014-07-05 03:44:15 +08:00
|
|
|
}
|
2014-10-15 22:23:53 +08:00
|
|
|
} else if (sla->prefix_len == 0) {
|
|
|
|
asla.sla = sla->sla;
|
|
|
|
asla.prefix_len = 0;
|
|
|
|
sla = &asla;
|
|
|
|
}
|
|
|
|
if (sla->prefix_len == 0) {
|
|
|
|
uint32_t sla_max;
|
|
|
|
int bits;
|
|
|
|
|
|
|
|
if (ia->sla_max == 0) {
|
|
|
|
const struct interface *ifi;
|
|
|
|
|
|
|
|
sla_max = 0;
|
|
|
|
TAILQ_FOREACH(ifi, ifp->ctx->ifaces, next) {
|
|
|
|
if (ifi != ifp && ifi->index > sla_max)
|
|
|
|
sla_max = ifi->index;
|
|
|
|
}
|
|
|
|
} else
|
|
|
|
sla_max = ia->sla_max;
|
|
|
|
|
|
|
|
bits = ffs32(sla_max);
|
|
|
|
|
2014-07-05 03:44:15 +08:00
|
|
|
if (prefix->prefix_len + bits > UINT8_MAX)
|
|
|
|
asla.prefix_len = UINT8_MAX;
|
|
|
|
else {
|
|
|
|
asla.prefix_len = prefix->prefix_len + (uint8_t)bits;
|
|
|
|
|
|
|
|
/* Make a 64 prefix by default, as this maks SLAAC
|
|
|
|
* possible. Otherwise round up to the nearest octet. */
|
|
|
|
if (asla.prefix_len <= 64)
|
|
|
|
asla.prefix_len = 64;
|
|
|
|
else
|
|
|
|
asla.prefix_len = ROUNDUP8(asla.prefix_len);
|
|
|
|
|
|
|
|
}
|
2014-10-15 22:23:53 +08:00
|
|
|
|
|
|
|
#define BIT(n) (1l << (n))
|
|
|
|
#define BIT_MASK(len) (BIT(len) - 1)
|
|
|
|
if (ia->sla_max == 0)
|
|
|
|
/* Work out the real sla_max from our bits used */
|
2014-10-16 00:02:05 +08:00
|
|
|
ia->sla_max = (uint32_t)BIT_MASK(asla.prefix_len -
|
2014-10-15 22:23:53 +08:00
|
|
|
prefix->prefix_len);
|
2014-07-05 03:44:15 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
if (ipv6_userprefix(&prefix->prefix, prefix->prefix_len,
|
|
|
|
sla->sla, addr, sla->prefix_len) == -1)
|
|
|
|
{
|
2014-10-15 22:23:53 +08:00
|
|
|
sa = inet_ntop(AF_INET6, &prefix->prefix,
|
|
|
|
sabuf, sizeof(sabuf));
|
2014-07-05 03:44:15 +08:00
|
|
|
syslog(LOG_ERR, "%s: invalid prefix %s/%d + %d/%d: %m",
|
2014-10-15 22:23:53 +08:00
|
|
|
ifp->name, sa, prefix->prefix_len,
|
2014-07-05 03:44:15 +08:00
|
|
|
sla->sla, sla->prefix_len);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2014-07-09 18:51:20 +08:00
|
|
|
if (prefix->prefix_exclude_len &&
|
|
|
|
IN6_ARE_ADDR_EQUAL(addr, &prefix->prefix_exclude))
|
|
|
|
{
|
2014-10-15 22:23:53 +08:00
|
|
|
sa = inet_ntop(AF_INET6, &prefix->prefix_exclude,
|
|
|
|
sabuf, sizeof(sabuf));
|
2014-07-09 18:51:20 +08:00
|
|
|
syslog(LOG_ERR, "%s: cannot delegate excluded prefix %s/%d",
|
2014-10-15 22:23:53 +08:00
|
|
|
ifp->name, sa, prefix->prefix_exclude_len);
|
2014-07-09 18:51:20 +08:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2014-07-08 00:42:30 +08:00
|
|
|
return sla->prefix_len;
|
2014-07-05 03:44:15 +08:00
|
|
|
}
|
|
|
|
|
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;
|
2014-07-05 03:44:15 +08:00
|
|
|
struct dhcp6_option *o, *so, *eo;
|
2013-06-05 18:37:45 +08:00
|
|
|
const struct dhcp6_option *si, *unicast;
|
2014-07-03 02:59:19 +08:00
|
|
|
size_t l, n, len, ml, auth_len;
|
2014-03-28 06:14:52 +08:00
|
|
|
uint8_t u8, type;
|
2014-09-29 04:34:44 +08:00
|
|
|
uint16_t u16, n_options;
|
2014-02-12 08:39:46 +08:00
|
|
|
struct if_options *ifo;
|
2014-07-03 02:59:19 +08:00
|
|
|
const struct dhcp_opt *opt, *opt2;
|
2014-07-09 18:51:20 +08:00
|
|
|
uint8_t IA, *p;
|
|
|
|
const uint8_t *pp;
|
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;
|
2014-07-11 18:13:52 +08:00
|
|
|
struct dhcp6_ia_addr *iap;
|
|
|
|
struct dhcp6_pd_addr *pdp;
|
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++)
|
2014-07-03 02:59:19 +08:00
|
|
|
{
|
|
|
|
for (n = 0, opt2 = ifo->dhcp6_override;
|
|
|
|
n < ifo->dhcp6_override_len;
|
|
|
|
n++, opt2++)
|
|
|
|
{
|
|
|
|
if (opt->option == opt2->option)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (n < ifo->dhcp6_override_len)
|
|
|
|
continue;
|
|
|
|
if (!(opt->type & NOREQ) &&
|
|
|
|
(opt->type & REQUEST ||
|
|
|
|
has_option_mask(ifo->requestmask6, opt->option)))
|
|
|
|
{
|
|
|
|
n_options++;
|
2014-09-29 04:34:44 +08:00
|
|
|
len += sizeof(u16);
|
2014-07-03 02:59:19 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
for (l = 0, opt = ifo->dhcp6_override;
|
|
|
|
l < ifo->dhcp6_override_len;
|
|
|
|
l++, opt++)
|
2014-02-12 08:39:46 +08:00
|
|
|
{
|
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++;
|
2014-09-29 04:34:44 +08:00
|
|
|
len += sizeof(u16);
|
2013-06-06 04:13:14 +08:00
|
|
|
}
|
2013-04-05 05:59:02 +08:00
|
|
|
}
|
2014-07-05 03:44:15 +08:00
|
|
|
if (dhcp6_findselfsla(ifp, NULL)) {
|
|
|
|
n_options++;
|
2014-09-29 04:34:44 +08:00
|
|
|
len += sizeof(u16);
|
2014-07-05 03:44:15 +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:
|
2014-06-21 19:42:36 +08:00
|
|
|
/* FALLTHROUGH */
|
|
|
|
case DH6S_DISCOVER:
|
2012-11-07 07:40:15 +08:00
|
|
|
if (m == NULL) {
|
|
|
|
m = state->new;
|
|
|
|
ml = state->new_len;
|
|
|
|
}
|
|
|
|
TAILQ_FOREACH(ap, &state->addrs, next) {
|
2014-06-21 19:42:36 +08:00
|
|
|
if (ap->prefix_vltime == 0 &&
|
|
|
|
!(ap->flags & IPV6_AF_REQUEST))
|
2013-06-12 06:49:07 +08:00
|
|
|
continue;
|
2014-07-04 19:53:56 +08:00
|
|
|
if (ap->ia_type == D6_OPTION_IA_PD) {
|
2014-07-05 03:44:15 +08:00
|
|
|
if (!(ifo->options & DHCPCD_NOPFXDLG)) {
|
2014-07-04 19:53:56 +08:00
|
|
|
len += sizeof(*o) + sizeof(u8) +
|
|
|
|
sizeof(u32) + sizeof(u32) +
|
2014-07-11 18:13:52 +08:00
|
|
|
sizeof(ap->prefix);
|
2014-07-09 18:51:20 +08:00
|
|
|
if (ap->prefix_exclude_len)
|
2014-07-05 03:44:15 +08:00
|
|
|
len += sizeof(*o) + 1 +
|
2014-07-09 18:51:20 +08:00
|
|
|
((ap->prefix_exclude_len -
|
2014-07-06 03:47:22 +08:00
|
|
|
ap->prefix_len - 1) / NBBY)
|
|
|
|
+ 1;
|
2014-07-05 03:44:15 +08:00
|
|
|
|
|
|
|
}
|
2014-07-04 19:53:56 +08:00
|
|
|
} else if (!(ifo->options & DHCPCD_PFXDLGONLY))
|
2014-07-11 18:13:52 +08:00
|
|
|
len += sizeof(*o) + sizeof(ap->addr) +
|
2013-04-01 20:15:47 +08:00
|
|
|
sizeof(u32) + sizeof(u32);
|
2012-11-07 07:40:15 +08:00
|
|
|
}
|
|
|
|
/* FALLTHROUGH */
|
2014-06-21 19:42:36 +08:00
|
|
|
case DH6S_INIT:
|
2014-07-04 19:53:56 +08:00
|
|
|
for (l = 0; l < ifo->ia_len; l++) {
|
|
|
|
if (ifo->ia[l].ia_type == D6_OPTION_IA_PD) {
|
|
|
|
if (ifo->options & DHCPCD_NOPFXDLG)
|
|
|
|
continue;
|
|
|
|
} else if (ifo->options & DHCPCD_PFXDLGONLY)
|
|
|
|
continue;
|
|
|
|
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) {
|
2014-03-28 06:14:52 +08:00
|
|
|
auth_len = (size_t)dhcp_auth_encode(&ifo->auth,
|
|
|
|
state->auth.token, NULL, 0, 6, type, NULL, 0);
|
2014-08-07 17:43:18 +08:00
|
|
|
if ((ssize_t)auth_len == -1) {
|
|
|
|
syslog(LOG_ERR, "%s: dhcp_auth_encode: %m",
|
|
|
|
ifp->name);
|
2014-03-28 06:14:52 +08:00
|
|
|
auth_len = 0;
|
2014-08-07 17:54:19 +08:00
|
|
|
} else if (auth_len != 0)
|
2014-01-25 09:35:53 +08:00
|
|
|
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 */
|
2014-07-11 18:13:52 +08:00
|
|
|
if (unicast && ntohs(unicast->len) == sizeof(state->unicast))
|
|
|
|
memcpy(&state->unicast, D6_COPTION_DATA(unicast),
|
|
|
|
sizeof(state->unicast));
|
2013-06-05 18:37:45 +08:00
|
|
|
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);
|
2014-03-28 03:12:03 +08:00
|
|
|
memset(p, 0, sizeof(uint16_t));
|
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++) {
|
2014-07-04 19:53:56 +08:00
|
|
|
if (ifo->ia[l].ia_type == D6_OPTION_IA_PD) {
|
|
|
|
if (ifo->options & DHCPCD_NOPFXDLG)
|
|
|
|
continue;
|
|
|
|
} else if (ifo->options & DHCPCD_PFXDLGONLY)
|
|
|
|
continue;
|
2012-11-07 07:40:15 +08:00
|
|
|
o = D6_NEXT_OPTION(o);
|
2014-07-02 04:34:19 +08:00
|
|
|
o->code = htons(ifo->ia[l].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) {
|
2014-06-21 19:42:36 +08:00
|
|
|
if (ap->prefix_vltime == 0 &&
|
|
|
|
!(ap->flags & IPV6_AF_REQUEST))
|
2013-06-12 06:49:07 +08:00
|
|
|
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);
|
2014-07-02 04:34:19 +08:00
|
|
|
if (ap->ia_type == D6_OPTION_IA_PD) {
|
2013-04-01 20:15:47 +08:00
|
|
|
so->code = htons(D6_OPTION_IAPREFIX);
|
2014-07-11 18:13:52 +08:00
|
|
|
so->len = htons(sizeof(ap->prefix) +
|
2013-04-01 20:15:47 +08:00
|
|
|
sizeof(u32) + sizeof(u32) + sizeof(u8));
|
2014-07-11 18:13:52 +08:00
|
|
|
pdp = (struct dhcp6_pd_addr *)
|
|
|
|
D6_OPTION_DATA(so);
|
|
|
|
pdp->pltime = htonl(ap->prefix_pltime);
|
|
|
|
pdp->vltime = htonl(ap->prefix_vltime);
|
|
|
|
pdp->prefix_len = ap->prefix_len;
|
|
|
|
pdp->prefix = ap->prefix;
|
2014-07-05 03:44:15 +08:00
|
|
|
|
2014-07-07 17:21:35 +08:00
|
|
|
/* RFC6603 Section 4.2 */
|
2014-07-09 18:51:20 +08:00
|
|
|
if (ap->prefix_exclude_len) {
|
|
|
|
n = ((ap->prefix_exclude_len -
|
2014-07-06 03:47:22 +08:00
|
|
|
ap->prefix_len - 1) / NBBY) + 1;
|
2014-07-05 03:44:15 +08:00
|
|
|
eo = D6_NEXT_OPTION(so);
|
|
|
|
eo->code = htons(D6_OPTION_PD_EXCLUDE);
|
2014-07-07 17:21:35 +08:00
|
|
|
eo->len = (uint16_t)n + 1;
|
2014-07-05 03:44:15 +08:00
|
|
|
p = D6_OPTION_DATA(eo);
|
2014-07-09 18:51:20 +08:00
|
|
|
*p++ = (uint8_t)ap->prefix_exclude_len;
|
|
|
|
pp = ap->prefix_exclude.s6_addr;
|
2014-07-07 17:21:35 +08:00
|
|
|
pp += ((ap->prefix_len - 1) / NBBY)
|
|
|
|
+ (n - 1);
|
2014-07-06 03:47:22 +08:00
|
|
|
u8 = ap->prefix_len % NBBY;
|
2014-07-07 17:21:35 +08:00
|
|
|
if (u8)
|
|
|
|
n--;
|
|
|
|
while (n-- > 0)
|
|
|
|
*p++ = *pp--;
|
|
|
|
if (u8)
|
|
|
|
*p = (uint8_t)(*pp << u8);
|
2014-07-05 03:44:15 +08:00
|
|
|
u32 = ntohs(so->len) +
|
|
|
|
sizeof(*eo) + eo->len;
|
|
|
|
so->len = htons(u32);
|
|
|
|
eo->len = htons(eo->len);
|
|
|
|
}
|
|
|
|
|
2013-04-01 20:15:47 +08:00
|
|
|
u32 = ntohs(o->len) + sizeof(*so)
|
|
|
|
+ ntohs(so->len);
|
|
|
|
o->len = htons(u32);
|
|
|
|
} else {
|
|
|
|
so->code = htons(D6_OPTION_IA_ADDR);
|
2014-07-11 18:13:52 +08:00
|
|
|
so->len = sizeof(ap->addr) +
|
2014-07-05 03:44:15 +08:00
|
|
|
sizeof(u32) + sizeof(u32);
|
2014-07-11 18:13:52 +08:00
|
|
|
iap = (struct dhcp6_ia_addr *)
|
|
|
|
D6_OPTION_DATA(so);
|
|
|
|
iap->addr = ap->addr;
|
|
|
|
iap->pltime = htonl(ap->prefix_pltime);
|
|
|
|
iap->vltime = htonl(ap->prefix_vltime);
|
2013-04-01 20:15:47 +08:00
|
|
|
u32 = ntohs(o->len) + sizeof(*so)
|
2014-07-05 03:44:15 +08:00
|
|
|
+ so->len;
|
|
|
|
so->len = htons(so->len);
|
2013-04-01 20:15:47 +08:00
|
|
|
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;
|
2014-09-29 04:34:44 +08:00
|
|
|
p = 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++)
|
2014-07-03 02:59:19 +08:00
|
|
|
{
|
|
|
|
for (n = 0, opt2 = ifo->dhcp6_override;
|
|
|
|
n < ifo->dhcp6_override_len;
|
|
|
|
n++, opt2++)
|
|
|
|
{
|
|
|
|
if (opt->option == opt2->option)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (n < ifo->dhcp6_override_len)
|
|
|
|
continue;
|
|
|
|
if (!(opt->type & NOREQ) &&
|
|
|
|
(opt->type & REQUEST ||
|
|
|
|
has_option_mask(ifo->requestmask6,
|
|
|
|
opt->option)))
|
|
|
|
{
|
2014-09-29 04:34:44 +08:00
|
|
|
u16 = htons(opt->option);
|
|
|
|
memcpy(p, &u16, sizeof(u16));
|
|
|
|
p += sizeof(u16);
|
|
|
|
o->len += sizeof(u16);
|
2014-07-03 02:59:19 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
for (l = 0, opt = ifo->dhcp6_override;
|
|
|
|
l < ifo->dhcp6_override_len;
|
|
|
|
l++, opt++)
|
2013-12-04 07:10:21 +08:00
|
|
|
{
|
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
|
|
|
{
|
2014-09-29 04:34:44 +08:00
|
|
|
u16 = htons(opt->option);
|
|
|
|
memcpy(p, &u16, sizeof(u16));
|
|
|
|
p += sizeof(u16);
|
|
|
|
o->len += sizeof(u16);
|
2013-06-06 04:13:14 +08:00
|
|
|
}
|
2013-06-06 01:36:49 +08:00
|
|
|
}
|
2014-07-05 03:44:15 +08:00
|
|
|
if (dhcp6_findselfsla(ifp, NULL)) {
|
2014-09-29 04:34:44 +08:00
|
|
|
u16 = htons(D6_OPTION_PD_EXCLUDE);
|
|
|
|
memcpy(p, &u16, sizeof(u16));
|
|
|
|
o->len += sizeof(u16);
|
2014-07-05 03:44:15 +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 */
|
2014-08-07 17:54:19 +08:00
|
|
|
if (ifo->auth.options & DHCPCD_AUTH_SEND && auth_len != 0) {
|
2014-01-25 09:35:53 +08:00
|
|
|
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-03-28 07:00:32 +08:00
|
|
|
static ssize_t
|
2014-03-28 06:14:52 +08:00
|
|
|
dhcp6_update_auth(struct interface *ifp, struct dhcp6_message *m, size_t len)
|
2014-01-25 10:23:41 +08:00
|
|
|
{
|
|
|
|
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;
|
2014-03-12 07:41:16 +08:00
|
|
|
struct sockaddr_in6 dst;
|
2012-10-12 18:31:51 +08:00
|
|
|
struct cmsghdr *cm;
|
|
|
|
struct in6_pktinfo pi;
|
2012-11-07 07:40:15 +08:00
|
|
|
struct timeval RTprev;
|
|
|
|
double rnd;
|
2014-03-28 06:14:52 +08:00
|
|
|
time_t ms;
|
2012-11-07 07:40:15 +08:00
|
|
|
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
|
|
|
|
2014-03-12 07:41:16 +08:00
|
|
|
memset(&dst, 0, sizeof(dst));
|
|
|
|
dst.sin6_family = AF_INET6;
|
|
|
|
dst.sin6_port = htons(DHCP6_SERVER_PORT);
|
2013-06-05 18:37:45 +08:00
|
|
|
#ifdef SIN6_LEN
|
2014-03-12 07:41:16 +08:00
|
|
|
dst.sin6_len = sizeof(dst);
|
2013-06-05 18:37:45 +08:00
|
|
|
#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-03-12 07:41:16 +08:00
|
|
|
dst.sin6_addr = alldhcp;
|
2013-06-05 18:37:45 +08:00
|
|
|
broad_uni = "broadcasting";
|
|
|
|
} else {
|
2014-03-12 07:41:16 +08:00
|
|
|
dst.sin6_addr = state->unicast;
|
2013-06-05 18:37:45 +08:00
|
|
|
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;
|
2014-05-25 03:58:50 +08:00
|
|
|
state->RT.tv_usec = (suseconds_t)arc4random_uniform(
|
|
|
|
state->IMD * 1000000);
|
2013-06-12 23:01:59 +08:00
|
|
|
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;
|
2014-05-25 03:58:50 +08:00
|
|
|
rnd += (suseconds_t)arc4random_uniform(
|
|
|
|
DHCP6_RAND_MAX - DHCP6_RAND_MIN);
|
2012-11-07 07:40:15 +08:00
|
|
|
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);
|
2014-08-07 17:43:18 +08:00
|
|
|
if (errno != ESRCH)
|
|
|
|
return -1;
|
2014-01-25 10:23:41 +08:00
|
|
|
}
|
2012-11-07 07:40:15 +08:00
|
|
|
|
2014-02-12 08:39:46 +08:00
|
|
|
ctx = ifp->ctx->ipv6;
|
2014-03-12 07:41:16 +08:00
|
|
|
dst.sin6_scope_id = ifp->index;
|
2014-09-18 16:42:41 +08:00
|
|
|
ctx->sndhdr.msg_name = (void *)&dst;
|
|
|
|
ctx->sndhdr.msg_iov[0].iov_base = state->send;
|
2014-02-12 08:39:46 +08:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2014-09-05 03:55:37 +08:00
|
|
|
int
|
|
|
|
dhcp6_dadcompleted(const struct interface *ifp)
|
|
|
|
{
|
|
|
|
const struct dhcp6_state *state;
|
|
|
|
const struct ipv6_addr *ap;
|
|
|
|
|
|
|
|
state = D6_CSTATE(ifp);
|
2014-10-04 01:54:06 +08:00
|
|
|
if (!TAILQ_FIRST(&state->addrs))
|
|
|
|
return 0;
|
|
|
|
|
2014-09-05 03:55:37 +08:00
|
|
|
TAILQ_FOREACH(ap, &state->addrs, next) {
|
|
|
|
if (ap->flags & IPV6_AF_ADDED &&
|
|
|
|
!(ap->flags & IPV6_AF_DADCOMPLETED))
|
2014-09-24 21:03:11 +08:00
|
|
|
return 0;
|
2014-09-05 03:55:37 +08:00
|
|
|
}
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2014-06-21 19:42:36 +08:00
|
|
|
static void
|
|
|
|
dhcp6_dadcallback(void *arg)
|
|
|
|
{
|
|
|
|
struct ipv6_addr *ap = arg;
|
|
|
|
struct interface *ifp;
|
|
|
|
struct dhcp6_state *state;
|
2014-10-02 23:20:07 +08:00
|
|
|
int wascompleted, valid;
|
2014-06-21 19:42:36 +08:00
|
|
|
|
|
|
|
wascompleted = (ap->flags & IPV6_AF_DADCOMPLETED);
|
|
|
|
ap->flags |= IPV6_AF_DADCOMPLETED;
|
|
|
|
if (ap->flags & IPV6_AF_DUPLICATED)
|
|
|
|
/* XXX FIXME
|
|
|
|
* We should decline the address */
|
|
|
|
syslog(LOG_WARNING, "%s: DAD detected %s",
|
|
|
|
ap->iface->name, ap->saddr);
|
|
|
|
|
|
|
|
if (!wascompleted) {
|
|
|
|
ifp = ap->iface;
|
|
|
|
state = D6_STATE(ifp);
|
|
|
|
if (state->state == DH6S_BOUND ||
|
|
|
|
state->state == DH6S_DELEGATED)
|
|
|
|
{
|
2014-10-17 20:37:44 +08:00
|
|
|
struct ipv6_addr *ap2;
|
|
|
|
|
2014-10-02 23:20:07 +08:00
|
|
|
valid = (ap->delegating_iface == NULL);
|
2014-10-17 20:37:44 +08:00
|
|
|
TAILQ_FOREACH(ap2, &state->addrs, next) {
|
|
|
|
if (ap2->flags & IPV6_AF_ADDED &&
|
|
|
|
!(ap2->flags & IPV6_AF_DADCOMPLETED))
|
2014-07-09 03:28:52 +08:00
|
|
|
{
|
2014-06-21 19:42:36 +08:00
|
|
|
wascompleted = 1;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (!wascompleted) {
|
|
|
|
syslog(LOG_DEBUG, "%s: DHCPv6 DAD completed",
|
|
|
|
ifp->name);
|
2014-10-17 20:37:44 +08:00
|
|
|
script_runreason(ifp,
|
|
|
|
ap->delegating_iface ?
|
|
|
|
"DELEGATED6" : state->reason);
|
2014-10-02 23:20:07 +08:00
|
|
|
if (valid)
|
2014-10-02 18:54:02 +08:00
|
|
|
dhcpcd_daemonise(ifp->ctx);
|
2014-06-21 19:42:36 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-11-07 07:40:15 +08:00
|
|
|
static void
|
2014-07-02 04:34:19 +08:00
|
|
|
dhcp6_addrequestedaddrs(struct interface *ifp)
|
2012-11-07 07:40:15 +08:00
|
|
|
{
|
|
|
|
struct dhcp6_state *state;
|
2014-06-21 19:42:36 +08:00
|
|
|
size_t i;
|
|
|
|
struct if_ia *ia;
|
|
|
|
struct ipv6_addr *a;
|
2014-07-02 04:34:19 +08:00
|
|
|
char iabuf[INET6_ADDRSTRLEN];
|
|
|
|
const char *iap;
|
|
|
|
|
|
|
|
state = D6_STATE(ifp);
|
|
|
|
/* Add any requested prefixes / addresses */
|
|
|
|
for (i = 0; i < ifp->options->ia_len; i++) {
|
|
|
|
ia = &ifp->options->ia[i];
|
|
|
|
if (!((ia->ia_type == D6_OPTION_IA_PD && ia->prefix_len) ||
|
|
|
|
!IN6_IS_ADDR_UNSPECIFIED(&ia->addr)))
|
|
|
|
continue;
|
|
|
|
a = calloc(1, sizeof(*a));
|
|
|
|
if (a == NULL) {
|
|
|
|
syslog(LOG_ERR, "%s: %m", __func__);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
a->flags = IPV6_AF_REQUEST;
|
|
|
|
a->iface = ifp;
|
|
|
|
a->dadcallback = dhcp6_dadcallback;
|
|
|
|
memcpy(&a->iaid, &ia->iaid, sizeof(a->iaid));
|
|
|
|
a->ia_type = ia->ia_type;
|
|
|
|
//a->prefix_pltime = 0;
|
|
|
|
//a->prefix_vltime = 0;
|
|
|
|
|
|
|
|
if (ia->ia_type == D6_OPTION_IA_PD) {
|
|
|
|
memcpy(&a->prefix, &ia->addr, sizeof(a->addr));
|
|
|
|
a->prefix_len = ia->prefix_len;
|
2014-07-11 18:13:52 +08:00
|
|
|
iap = inet_ntop(AF_INET6, &a->prefix,
|
2014-07-02 04:34:19 +08:00
|
|
|
iabuf, sizeof(iabuf));
|
|
|
|
} else {
|
|
|
|
memcpy(&a->addr, &ia->addr, sizeof(a->addr));
|
|
|
|
/*
|
|
|
|
* 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;
|
|
|
|
ipv6_makeprefix(&a->prefix, &a->addr, a->prefix_len);
|
2014-07-11 18:13:52 +08:00
|
|
|
iap = inet_ntop(AF_INET6, &a->addr,
|
2014-07-02 04:34:19 +08:00
|
|
|
iabuf, sizeof(iabuf));
|
|
|
|
}
|
|
|
|
snprintf(a->saddr, sizeof(a->saddr),
|
|
|
|
"%s/%d", iap, a->prefix_len);
|
|
|
|
TAILQ_INSERT_TAIL(&state->addrs, a, next);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
dhcp6_startdiscover(void *arg)
|
|
|
|
{
|
|
|
|
struct interface *ifp;
|
|
|
|
struct dhcp6_state *state;
|
2012-11-07 07:40:15 +08:00
|
|
|
|
|
|
|
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
|
|
|
|
2014-07-02 04:34:19 +08:00
|
|
|
dhcp6_addrequestedaddrs(ifp);
|
2014-06-21 19:42:36 +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);
|
|
|
|
}
|
|
|
|
|
2014-07-02 04:34:19 +08:00
|
|
|
|
|
|
|
static int
|
|
|
|
dhcp6_hasprefixdelegation(struct interface *ifp)
|
|
|
|
{
|
|
|
|
size_t i;
|
2014-07-09 12:41:23 +08:00
|
|
|
uint16_t t;
|
2014-07-02 04:34:19 +08:00
|
|
|
|
2014-07-07 18:01:24 +08:00
|
|
|
if (ifp->options->options & DHCPCD_NOPFXDLG)
|
|
|
|
return 0;
|
|
|
|
|
2014-07-09 12:41:23 +08:00
|
|
|
t = 0;
|
2014-07-02 04:34:19 +08:00
|
|
|
for (i = 0; i < ifp->options->ia_len; i++) {
|
2014-07-09 12:41:23 +08:00
|
|
|
if (t && t != ifp->options->ia[i].ia_type) {
|
|
|
|
if (t == D6_OPTION_IA_PD ||
|
|
|
|
ifp->options->ia[i].ia_type == D6_OPTION_IA_PD)
|
|
|
|
return 2;
|
|
|
|
}
|
|
|
|
t = ifp->options->ia[i].ia_type;
|
2014-07-02 04:34:19 +08:00
|
|
|
}
|
2014-07-09 12:41:23 +08:00
|
|
|
return t == D6_OPTION_IA_PD ? 1 : 0;
|
2014-07-02 04:34:19 +08:00
|
|
|
}
|
|
|
|
|
2013-06-10 05:51:52 +08:00
|
|
|
static void
|
|
|
|
dhcp6_startrebind(void *arg)
|
|
|
|
{
|
|
|
|
struct interface *ifp;
|
|
|
|
struct dhcp6_state *state;
|
2014-07-02 04:34:19 +08:00
|
|
|
int pd;
|
2013-06-10 05:51:52 +08:00
|
|
|
|
|
|
|
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);
|
2014-07-02 04:34:19 +08:00
|
|
|
else
|
2014-07-08 16:41:08 +08:00
|
|
|
syslog(LOG_INFO, "%s: rebinding prior DHCPv6 lease",
|
2014-07-02 04:34:19 +08:00
|
|
|
ifp->name);
|
2013-06-10 05:51:52 +08:00
|
|
|
state->state = DH6S_REBIND;
|
|
|
|
state->RTC = 0;
|
|
|
|
state->MRC = 0;
|
|
|
|
|
|
|
|
/* RFC 3633 section 12.1 */
|
2014-07-02 04:34:19 +08:00
|
|
|
pd = dhcp6_hasprefixdelegation(ifp);
|
|
|
|
if (pd) {
|
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 */
|
2014-07-02 04:34:19 +08:00
|
|
|
if (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");
|
2014-09-24 20:23:30 +08:00
|
|
|
if (ipv6nd_hasradhcp(ifp) || dhcp6_hasprefixdelegation(ifp))
|
2014-05-19 08:49:14 +08:00
|
|
|
dhcp6_startdiscover(ifp);
|
|
|
|
else
|
|
|
|
syslog(LOG_WARNING,
|
|
|
|
"%s: no advertising IPv6 router wants DHCP",
|
|
|
|
ifp->name);
|
2012-11-07 07:40:15 +08:00
|
|
|
}
|
|
|
|
|
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 *
|
2014-10-10 02:59:30 +08:00
|
|
|
dhcp6_iffindaddr(struct interface *ifp, const struct in6_addr *addr,
|
|
|
|
short flags)
|
2012-11-07 07:40:15 +08:00
|
|
|
{
|
2014-10-10 02:59:30 +08:00
|
|
|
struct dhcp6_state *state;
|
2013-05-24 05:50:34 +08:00
|
|
|
struct ipv6_addr *ap;
|
2012-11-07 07:40:15 +08:00
|
|
|
|
2014-10-10 02:59:30 +08:00
|
|
|
state = D6_STATE(ifp);
|
2013-05-24 05:50:34 +08:00
|
|
|
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;
|
2014-10-10 02:59:30 +08:00
|
|
|
} else if (ap->prefix_vltime &&
|
|
|
|
IN6_ARE_ADDR_EQUAL(&ap->addr, addr) &&
|
|
|
|
(!flags || ap->flags & flags))
|
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;
|
|
|
|
}
|
|
|
|
|
2014-10-10 02:59:30 +08:00
|
|
|
struct ipv6_addr *
|
|
|
|
dhcp6_findaddr(struct dhcpcd_ctx *ctx, const struct in6_addr *addr,
|
|
|
|
short flags)
|
2013-05-24 05:50:34 +08:00
|
|
|
{
|
|
|
|
struct interface *ifp;
|
2014-10-10 02:59:30 +08:00
|
|
|
struct ipv6_addr *ap;
|
2013-05-24 05:50:34 +08:00
|
|
|
|
2014-02-12 08:39:46 +08:00
|
|
|
TAILQ_FOREACH(ifp, ctx->ifaces, next) {
|
2014-10-10 02:59:30 +08:00
|
|
|
ap = dhcp6_iffindaddr(ifp, addr, flags);
|
|
|
|
if (ap)
|
|
|
|
return ap;
|
2013-05-24 05:50:34 +08:00
|
|
|
}
|
2014-10-10 02:59:30 +08:00
|
|
|
return NULL;
|
2012-11-07 07:40:15 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
2014-07-02 04:34:19 +08:00
|
|
|
dhcp6_findna(struct interface *ifp, uint16_t ot, const uint8_t *iaid,
|
2014-12-17 04:20:01 +08:00
|
|
|
const uint8_t *d, size_t l, const struct timeval *acquired)
|
2012-11-07 07:40:15 +08:00
|
|
|
{
|
|
|
|
struct dhcp6_state *state;
|
|
|
|
const struct dhcp6_option *o;
|
|
|
|
struct ipv6_addr *a;
|
|
|
|
char iabuf[INET6_ADDRSTRLEN];
|
|
|
|
const char *ia;
|
|
|
|
int i;
|
|
|
|
uint32_t u32;
|
2014-03-28 06:14:52 +08:00
|
|
|
size_t off;
|
2014-07-11 18:13:52 +08:00
|
|
|
const struct dhcp6_ia_addr *iap;
|
2012-11-07 07:40:15 +08:00
|
|
|
|
|
|
|
i = 0;
|
|
|
|
state = D6_STATE(ifp);
|
|
|
|
while ((o = dhcp6_findoption(D6_OPTION_IA_ADDR, d, l))) {
|
2014-03-28 06:14:52 +08:00
|
|
|
off = (size_t)((const uint8_t *)o - d);
|
|
|
|
l -= off;
|
|
|
|
d += off;
|
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;
|
|
|
|
}
|
2014-07-14 20:55:19 +08:00
|
|
|
iap = (const struct dhcp6_ia_addr *)D6_COPTION_DATA(o);
|
2014-10-10 02:59:30 +08:00
|
|
|
a = dhcp6_iffindaddr(ifp, &iap->addr, 0);
|
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;
|
2014-07-02 04:34:19 +08:00
|
|
|
a->ia_type = ot;
|
2013-05-24 05:50:34 +08:00
|
|
|
memcpy(a->iaid, iaid, sizeof(a->iaid));
|
2014-07-11 18:13:52 +08:00
|
|
|
a->addr = iap->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;
|
2014-07-02 04:34:19 +08:00
|
|
|
ipv6_makeprefix(&a->prefix, &a->addr, a->prefix_len);
|
2014-07-11 18:13:52 +08:00
|
|
|
ia = inet_ntop(AF_INET6, &a->addr,
|
2013-10-10 18:09:48 +08:00
|
|
|
iabuf, sizeof(iabuf));
|
|
|
|
snprintf(a->saddr, sizeof(a->saddr),
|
|
|
|
"%s/%d", ia, a->prefix_len);
|
2014-07-02 04:34:19 +08:00
|
|
|
|
2014-02-28 18:34:05 +08:00
|
|
|
TAILQ_INSERT_TAIL(&state->addrs, a, next);
|
2014-07-02 04:34:19 +08:00
|
|
|
} else {
|
|
|
|
if (!(a->flags & IPV6_AF_ONLINK))
|
|
|
|
a->flags |= IPV6_AF_ONLINK | IPV6_AF_NEW;
|
|
|
|
a->flags &= ~IPV6_AF_STALE;
|
|
|
|
}
|
2014-12-17 04:20:01 +08:00
|
|
|
a->acquired = *acquired;
|
2014-07-11 18:13:52 +08:00
|
|
|
a->prefix_pltime = ntohl(iap->pltime);
|
|
|
|
u32 = ntohl(iap->vltime);
|
2013-10-10 18:09:48 +08:00
|
|
|
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,
|
2014-12-17 04:20:01 +08:00
|
|
|
const uint8_t *d, size_t l, const struct timeval *acquired)
|
2013-04-01 20:15:47 +08:00
|
|
|
{
|
|
|
|
struct dhcp6_state *state;
|
2014-07-05 03:44:15 +08:00
|
|
|
const struct dhcp6_option *o, *ex;
|
2014-07-11 18:13:52 +08:00
|
|
|
const uint8_t *p, *op;
|
2013-04-01 20:15:47 +08:00
|
|
|
struct ipv6_addr *a;
|
|
|
|
char iabuf[INET6_ADDRSTRLEN];
|
|
|
|
const char *ia;
|
|
|
|
int i;
|
2014-07-14 20:55:19 +08:00
|
|
|
uint8_t u8, *pw;
|
2014-03-28 06:14:52 +08:00
|
|
|
size_t off;
|
2014-07-09 18:51:20 +08:00
|
|
|
uint16_t ol;
|
2014-07-11 18:13:52 +08:00
|
|
|
const struct dhcp6_pd_addr *pdp;
|
2013-04-01 20:15:47 +08:00
|
|
|
|
|
|
|
i = 0;
|
|
|
|
state = D6_STATE(ifp);
|
|
|
|
while ((o = dhcp6_findoption(D6_OPTION_IAPREFIX, d, l))) {
|
2014-03-28 06:14:52 +08:00
|
|
|
off = (size_t)((const uint8_t *)o - d);
|
|
|
|
l -= off;
|
|
|
|
d += off;
|
2014-07-11 18:13:52 +08:00
|
|
|
ol = ntohs(o->len);
|
|
|
|
l -= sizeof(*o) + ol;
|
|
|
|
d += sizeof(*o) + ol;
|
|
|
|
if (ol < sizeof(*pdp)) {
|
2013-04-05 04:31:04 +08:00
|
|
|
errno = EINVAL;
|
|
|
|
syslog(LOG_ERR, "%s: IA Prefix option truncated",
|
|
|
|
ifp->name);
|
|
|
|
continue;
|
|
|
|
}
|
2013-10-10 18:09:48 +08:00
|
|
|
|
2014-07-11 18:13:52 +08:00
|
|
|
pdp = (const struct dhcp6_pd_addr *)D6_COPTION_DATA(o);
|
2013-10-10 18:09:48 +08:00
|
|
|
TAILQ_FOREACH(a, &state->addrs, next) {
|
2014-07-11 18:13:52 +08:00
|
|
|
if (IN6_ARE_ADDR_EQUAL(&a->prefix, &pdp->prefix))
|
2013-10-10 18:09:48 +08:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (a == NULL) {
|
|
|
|
a = calloc(1, sizeof(*a));
|
|
|
|
if (a == NULL) {
|
|
|
|
syslog(LOG_ERR, "%s: %m", __func__);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
a->iface = ifp;
|
2014-03-21 00:36:03 +08:00
|
|
|
a->flags = IPV6_AF_NEW | IPV6_AF_DELEGATEDPFX;
|
2013-10-10 18:09:48 +08:00
|
|
|
a->dadcallback = dhcp6_dadcallback;
|
2014-07-02 04:34:19 +08:00
|
|
|
a->ia_type = D6_OPTION_IA_PD;
|
2013-10-10 18:09:48 +08:00
|
|
|
memcpy(a->iaid, iaid, sizeof(a->iaid));
|
2014-07-11 18:13:52 +08:00
|
|
|
a->prefix = pdp->prefix;
|
|
|
|
a->prefix_len = pdp->prefix_len;
|
|
|
|
ia = inet_ntop(AF_INET6, &a->prefix,
|
2013-10-10 18:09:48 +08:00
|
|
|
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 {
|
2014-07-02 04:34:19 +08:00
|
|
|
if (!(a->flags & IPV6_AF_DELEGATEDPFX))
|
|
|
|
a->flags |= IPV6_AF_NEW | IPV6_AF_DELEGATEDPFX;
|
2014-06-21 19:42:36 +08:00
|
|
|
a->flags &= ~(IPV6_AF_STALE | IPV6_AF_REQUEST);
|
2014-07-11 18:13:52 +08:00
|
|
|
if (a->prefix_vltime != ntohl(pdp->vltime))
|
2014-02-28 18:34:05 +08:00
|
|
|
a->flags |= IPV6_AF_NEW;
|
|
|
|
}
|
2013-10-10 18:09:48 +08:00
|
|
|
|
2014-12-17 04:20:01 +08:00
|
|
|
a->acquired = *acquired;
|
2014-07-11 18:13:52 +08:00
|
|
|
a->prefix_pltime = ntohl(pdp->pltime);
|
|
|
|
a->prefix_vltime = ntohl(pdp->vltime);
|
2014-07-02 04:34:19 +08:00
|
|
|
|
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++;
|
2014-07-05 03:44:15 +08:00
|
|
|
|
2014-07-14 20:55:19 +08:00
|
|
|
p = D6_COPTION_DATA(o) + sizeof(pdp);
|
2014-07-11 18:13:52 +08:00
|
|
|
ol -= sizeof(pdp);
|
|
|
|
ex = dhcp6_findoption(D6_OPTION_PD_EXCLUDE, p, ol);
|
2014-07-09 18:51:20 +08:00
|
|
|
a->prefix_exclude_len = 0;
|
|
|
|
memset(&a->prefix_exclude, 0, sizeof(a->prefix_exclude));
|
2014-07-11 18:13:52 +08:00
|
|
|
#if 0
|
2014-07-07 17:21:35 +08:00
|
|
|
if (ex == NULL) {
|
|
|
|
struct dhcp6_option *w;
|
|
|
|
uint8_t *wp;
|
|
|
|
|
|
|
|
w = calloc(1, 128);
|
|
|
|
w->len = htons(2);
|
|
|
|
wp = D6_OPTION_DATA(w);
|
|
|
|
*wp++ = 64;
|
|
|
|
*wp++ = 0x78;
|
|
|
|
ex = w;
|
|
|
|
}
|
|
|
|
#endif
|
2014-07-09 18:51:20 +08:00
|
|
|
if (ex == NULL)
|
|
|
|
continue;
|
|
|
|
ol = ntohs(ex->len);
|
|
|
|
if (ol < 2) {
|
|
|
|
syslog(LOG_ERR, "%s: truncated PD Exclude",
|
|
|
|
ifp->name);
|
|
|
|
continue;
|
2014-07-05 03:44:15 +08:00
|
|
|
}
|
2014-07-09 18:51:20 +08:00
|
|
|
op = D6_COPTION_DATA(ex);
|
|
|
|
a->prefix_exclude_len = *op++;
|
|
|
|
ol--;
|
|
|
|
if (((a->prefix_exclude_len - a->prefix_len - 1) / NBBY) + 1
|
|
|
|
!= ol)
|
|
|
|
{
|
|
|
|
syslog(LOG_ERR, "%s: PD Exclude length mismatch",
|
|
|
|
ifp->name);
|
2014-07-05 03:44:15 +08:00
|
|
|
a->prefix_exclude_len = 0;
|
2014-07-09 18:51:20 +08:00
|
|
|
continue;
|
2014-07-05 03:44:15 +08:00
|
|
|
}
|
2014-07-09 18:51:20 +08:00
|
|
|
u8 = a->prefix_len % NBBY;
|
|
|
|
memcpy(&a->prefix_exclude, &a->prefix,
|
|
|
|
sizeof(a->prefix_exclude));
|
|
|
|
if (u8)
|
|
|
|
ol--;
|
|
|
|
pw = a->prefix_exclude.s6_addr +
|
|
|
|
(a->prefix_exclude_len / NBBY) - 1;
|
|
|
|
while (ol-- > 0)
|
|
|
|
*pw-- = *op++;
|
|
|
|
if (u8)
|
|
|
|
*pw |= *op >> u8;
|
2013-04-01 20:15:47 +08:00
|
|
|
}
|
|
|
|
return i;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
2014-07-02 04:34:19 +08:00
|
|
|
dhcp6_findia(struct interface *ifp, const struct dhcp6_message *m, size_t l,
|
2014-12-17 04:20:01 +08:00
|
|
|
const char *sfrom, const struct timeval *acquired)
|
2013-04-01 20:15:47 +08:00
|
|
|
{
|
|
|
|
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;
|
2014-07-02 04:34:19 +08:00
|
|
|
size_t j;
|
2013-04-01 20:15:47 +08:00
|
|
|
uint32_t u32, renew, rebind;
|
2014-07-02 04:34:19 +08:00
|
|
|
uint16_t code, ol;
|
2013-04-01 20:15:47 +08:00
|
|
|
uint8_t iaid[4];
|
2014-07-02 04:34:19 +08:00
|
|
|
char buf[sizeof(iaid) * 3];
|
2013-05-24 05:50:34 +08:00
|
|
|
struct ipv6_addr *ap, *nap;
|
2013-04-01 20:15:47 +08:00
|
|
|
|
2014-07-02 04:34:19 +08:00
|
|
|
if (l < sizeof(*m)) {
|
2014-07-09 03:28:52 +08:00
|
|
|
/* Should be impossible with guards at packet in
|
|
|
|
* and reading leases */
|
2014-07-02 04:34:19 +08:00
|
|
|
errno = EINVAL;
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
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
|
|
|
}
|
2014-07-02 04:34:19 +08:00
|
|
|
l -= sizeof(*m);
|
2014-07-07 17:36:17 +08:00
|
|
|
for (o = D6_CFIRST_OPTION(m); l > sizeof(*o); o = D6_CNEXT_OPTION(o)) {
|
2013-12-04 21:21:45 +08:00
|
|
|
ol = ntohs(o->len);
|
2014-07-02 04:34:19 +08:00
|
|
|
if (sizeof(*o) + ol > l) {
|
|
|
|
errno = EINVAL;
|
|
|
|
syslog(LOG_ERR, "%s: option overflow", ifp->name);
|
|
|
|
break;
|
|
|
|
}
|
2013-04-05 04:31:04 +08:00
|
|
|
l -= sizeof(*o) + ol;
|
2014-07-02 04:34:19 +08:00
|
|
|
|
|
|
|
code = ntohs(o->code);
|
|
|
|
switch(code) {
|
|
|
|
case D6_OPTION_IA_TA:
|
|
|
|
u32 = 4;
|
|
|
|
break;
|
|
|
|
case D6_OPTION_IA_NA:
|
|
|
|
case D6_OPTION_IA_PD:
|
|
|
|
u32 = 12;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
continue;
|
|
|
|
}
|
2013-04-05 04:31:04 +08:00
|
|
|
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);
|
2014-07-02 04:34:19 +08:00
|
|
|
|
|
|
|
for (j = 0; j < ifo->ia_len; j++) {
|
|
|
|
if (memcmp(&ifo->ia[j].iaid, iaid, sizeof(iaid)) == 0)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (j == ifo->ia_len) {
|
|
|
|
syslog(LOG_DEBUG, "%s: ignoring unrequested IAID %s",
|
|
|
|
ifp->name,
|
|
|
|
hwaddr_ntoa(iaid, sizeof(iaid), buf, sizeof(buf)));
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (ifo->ia[j].ia_type != code) {
|
|
|
|
syslog(LOG_ERR, "%s: IAID %s: option type mismatch",
|
|
|
|
ifp->name,
|
|
|
|
hwaddr_ntoa(iaid, sizeof(iaid), buf, sizeof(buf)));
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (code != 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-07-02 04:34:19 +08:00
|
|
|
if (code == D6_OPTION_IA_PD) {
|
2014-07-04 19:53:56 +08:00
|
|
|
if (!(ifo->options & DHCPCD_NOPFXDLG) &&
|
2014-12-17 04:20:01 +08:00
|
|
|
dhcp6_findpd(ifp, iaid, p, ol, acquired) == 0)
|
2014-07-04 19:53:56 +08:00
|
|
|
{
|
2014-02-28 18:34:05 +08:00
|
|
|
syslog(LOG_WARNING,
|
|
|
|
"%s: %s: DHCPv6 REPLY missing Prefix",
|
|
|
|
ifp->name, sfrom);
|
|
|
|
continue;
|
|
|
|
}
|
2014-07-04 19:53:56 +08:00
|
|
|
} else if (!(ifo->options & DHCPCD_PFXDLGONLY)) {
|
2014-12-17 04:20:01 +08:00
|
|
|
if (dhcp6_findna(ifp, code, iaid, p, ol, acquired) == 0)
|
|
|
|
{
|
2014-02-28 18:34:05 +08:00
|
|
|
syslog(LOG_WARNING,
|
|
|
|
"%s: %s: DHCPv6 REPLY missing IA Address",
|
|
|
|
ifp->name, sfrom);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
2014-07-02 04:34:19 +08:00
|
|
|
if (code != 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) {
|
|
|
|
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);
|
2014-07-02 04:34:19 +08:00
|
|
|
if (ap->flags & IPV6_AF_REQUEST) {
|
|
|
|
ap->prefix_vltime = ap->prefix_pltime = 0;
|
|
|
|
} else {
|
|
|
|
TAILQ_REMOVE(&state->addrs, ap, next);
|
|
|
|
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,
|
2014-12-17 04:20:01 +08:00
|
|
|
const char *sfrom, const struct timeval *acquired)
|
2012-11-07 07:40:15 +08:00
|
|
|
{
|
|
|
|
struct dhcp6_state *state;
|
2014-07-28 17:15:04 +08:00
|
|
|
int nia;
|
2014-12-17 04:20:01 +08:00
|
|
|
struct timeval aq;
|
2012-11-07 07:40:15 +08:00
|
|
|
|
2014-07-08 15:56:39 +08:00
|
|
|
if (len <= sizeof(*m)) {
|
|
|
|
syslog(LOG_ERR, "%s: DHCPv6 lease truncated", ifp->name);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2012-11-07 07:40:15 +08:00
|
|
|
state = D6_STATE(ifp);
|
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;
|
2014-12-17 04:20:01 +08:00
|
|
|
if (!acquired) {
|
|
|
|
get_monotonic(&aq);
|
|
|
|
acquired = &aq;
|
|
|
|
}
|
|
|
|
nia = dhcp6_findia(ifp, m, len, sfrom, acquired);
|
2014-07-28 17:15:04 +08:00
|
|
|
if (nia == 0) {
|
|
|
|
syslog(LOG_ERR, "%s: no useable IA found in lease",
|
|
|
|
ifp->name);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
return nia;
|
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;
|
2014-12-17 04:20:01 +08:00
|
|
|
struct timeval acquired;
|
2012-11-07 07:40:15 +08:00
|
|
|
|
|
|
|
state = D6_STATE(ifp);
|
|
|
|
if (stat(state->leasefile, &st) == -1) {
|
|
|
|
if (errno == ENOENT)
|
|
|
|
return 0;
|
2014-06-22 21:05:05 +08:00
|
|
|
syslog(LOG_ERR, "%s: %s: %m", ifp->name, __func__);
|
2012-11-07 07:40:15 +08:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
syslog(LOG_DEBUG, "%s: reading lease `%s'",
|
|
|
|
ifp->name, state->leasefile);
|
2014-03-28 07:15:14 +08:00
|
|
|
if (st.st_size > UINT32_MAX) {
|
2014-03-28 06:14:52 +08:00
|
|
|
syslog(LOG_ERR, "%s: file too big", ifp->name);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
state->new = malloc((size_t)st.st_size);
|
2014-06-22 21:05:05 +08:00
|
|
|
if (state->new == NULL) {
|
|
|
|
syslog(LOG_ERR, "%s: %m", __func__);
|
2012-11-07 07:40:15 +08:00
|
|
|
return -1;
|
2014-06-22 21:05:05 +08:00
|
|
|
}
|
2014-03-28 06:14:52 +08:00
|
|
|
state->new_len = (size_t)st.st_size;
|
2012-11-07 07:40:15 +08:00
|
|
|
fd = open(state->leasefile, O_RDONLY);
|
2014-06-22 21:05:05 +08:00
|
|
|
if (fd == -1) {
|
|
|
|
syslog(LOG_ERR, "%s: %s: %s: %m", ifp->name, __func__,
|
|
|
|
state->leasefile);
|
2012-11-07 07:40:15 +08:00
|
|
|
return -1;
|
2014-06-22 21:05:05 +08:00
|
|
|
}
|
2012-11-07 07:40:15 +08:00
|
|
|
bytes = read(fd, state->new, state->new_len);
|
|
|
|
close(fd);
|
2014-07-08 15:56:39 +08:00
|
|
|
if (bytes != (ssize_t)state->new_len) {
|
2013-06-12 23:21:05 +08:00
|
|
|
syslog(LOG_ERR, "%s: read: %m", __func__);
|
|
|
|
goto ex;
|
|
|
|
}
|
2012-11-07 07:40:15 +08:00
|
|
|
|
2014-12-17 04:20:01 +08:00
|
|
|
gettimeofday(&now, NULL);
|
|
|
|
get_monotonic(&acquired);
|
|
|
|
acquired.tv_sec -= now.tv_sec - st.st_mtime;
|
|
|
|
|
2012-11-07 07:40:15 +08:00
|
|
|
/* Check to see if the lease is still valid */
|
2014-12-17 04:20:01 +08:00
|
|
|
fd = dhcp6_validatelease(ifp, state->new, state->new_len, NULL,
|
|
|
|
&acquired);
|
2013-06-12 18:58:28 +08:00
|
|
|
if (fd == -1)
|
|
|
|
goto ex;
|
2012-11-07 07:40:15 +08:00
|
|
|
|
2014-09-24 18:15:36 +08:00
|
|
|
if (!(ifp->ctx->options & DHCPCD_DUMPLEASE) &&
|
2014-09-24 17:49:09 +08:00
|
|
|
state->expire != ND6_INFINITE_LIFETIME)
|
|
|
|
{
|
2013-05-31 21:40:46 +08:00
|
|
|
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;
|
2014-07-03 17:47:42 +08:00
|
|
|
if (!(ifp->ctx->options & DHCPCD_DUMPLEASE))
|
|
|
|
unlink(state->leasefile);
|
2012-11-07 07:40:15 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2014-07-02 04:34:19 +08:00
|
|
|
|
2012-11-07 07:40:15 +08:00
|
|
|
static void
|
|
|
|
dhcp6_startinit(struct interface *ifp)
|
|
|
|
{
|
|
|
|
struct dhcp6_state *state;
|
|
|
|
int r;
|
2014-07-02 04:34:19 +08:00
|
|
|
uint8_t has_ta, has_non_ta;
|
|
|
|
size_t i;
|
2012-11-07 07:40:15 +08:00
|
|
|
|
|
|
|
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-07-02 04:34:19 +08:00
|
|
|
|
|
|
|
dhcp6_addrequestedaddrs(ifp);
|
|
|
|
has_ta = has_non_ta = 0;
|
|
|
|
for (i = 0; i < ifp->options->ia_len; i++) {
|
|
|
|
switch (ifp->options->ia[i].ia_type) {
|
|
|
|
case D6_OPTION_IA_TA:
|
|
|
|
has_ta = 1;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
has_non_ta = 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-02-12 08:39:46 +08:00
|
|
|
if (!(ifp->ctx->options & DHCPCD_TEST) &&
|
2014-07-02 04:34:19 +08:00
|
|
|
!(has_ta && !has_non_ta) &&
|
2013-04-05 22:55:50 +08:00
|
|
|
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 */
|
2014-07-02 04:34:19 +08:00
|
|
|
if (dhcp6_hasprefixdelegation(ifp))
|
2013-04-01 20:15:47 +08:00
|
|
|
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 struct ipv6_addr *
|
2014-07-05 03:44:15 +08:00
|
|
|
dhcp6_ifdelegateaddr(struct interface *ifp, struct ipv6_addr *prefix,
|
2014-10-15 22:23:53 +08:00
|
|
|
const struct if_sla *sla, struct if_ia *ia, struct interface *ifs)
|
2013-04-01 20:15:47 +08:00
|
|
|
{
|
|
|
|
struct dhcp6_state *state;
|
2013-06-04 06:02:51 +08:00
|
|
|
struct in6_addr addr;
|
2013-10-10 18:09:48 +08:00
|
|
|
struct ipv6_addr *a, *ap, *apn;
|
2014-10-15 22:23:53 +08:00
|
|
|
char sabuf[INET6_ADDRSTRLEN];
|
|
|
|
const char *sa;
|
2014-07-08 00:42:30 +08:00
|
|
|
int pfxlen;
|
2013-04-01 20:15:47 +08:00
|
|
|
|
2014-07-05 03:44:15 +08:00
|
|
|
/* RFC6603 Section 4.2 */
|
2014-07-09 03:28:52 +08:00
|
|
|
if (strcmp(ifp->name, ifs->name) == 0) {
|
2014-07-05 03:44:15 +08:00
|
|
|
if (prefix->prefix_exclude_len == 0) {
|
2014-07-08 00:42:30 +08:00
|
|
|
/* Don't spam the log automatically */
|
|
|
|
if (sla)
|
|
|
|
syslog(LOG_WARNING,
|
|
|
|
"%s: DHCPv6 server does not support "
|
|
|
|
"OPTION_PD_EXCLUDE",
|
|
|
|
ifp->name);
|
2013-04-01 20:15:47 +08:00
|
|
|
return NULL;
|
|
|
|
}
|
2014-07-09 03:28:52 +08:00
|
|
|
pfxlen = prefix->prefix_exclude_len;
|
2014-07-05 03:44:15 +08:00
|
|
|
memcpy(&addr, &prefix->prefix_exclude, sizeof(addr));
|
2014-10-15 22:23:53 +08:00
|
|
|
} else if ((pfxlen = dhcp6_delegateaddr(&addr, ifp, prefix,
|
|
|
|
sla, ia)) == -1)
|
2013-06-02 17:51:30 +08:00
|
|
|
return NULL;
|
2014-07-05 03:44:15 +08:00
|
|
|
|
2013-06-02 17:51:30 +08:00
|
|
|
|
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));
|
2014-12-17 04:20:01 +08:00
|
|
|
a->acquired = prefix->acquired;
|
2013-04-01 20:15:47 +08:00
|
|
|
a->prefix_pltime = prefix->prefix_pltime;
|
|
|
|
a->prefix_vltime = prefix->prefix_vltime;
|
2014-07-11 18:13:52 +08:00
|
|
|
a->prefix = addr;
|
2014-07-09 03:28:52 +08:00
|
|
|
a->prefix_len = (uint8_t)pfxlen;
|
2013-04-01 20:15:47 +08:00
|
|
|
|
2014-03-23 03:55:29 +08:00
|
|
|
/* Wang a 1 at the end as the prefix could be >64
|
|
|
|
* making SLAAC impossible. */
|
2014-07-11 18:13:52 +08:00
|
|
|
a->addr = a->prefix;
|
2014-03-23 03:55:29 +08:00
|
|
|
a->addr.s6_addr[sizeof(a->addr.s6_addr) - 1] += 1;
|
2013-04-01 20:15:47 +08:00
|
|
|
|
2014-07-05 03:44:15 +08:00
|
|
|
state = D6_STATE(ifp);
|
2013-04-01 20:15:47 +08:00
|
|
|
/* 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);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-10-15 22:23:53 +08:00
|
|
|
sa = inet_ntop(AF_INET6, &a->addr, sabuf, sizeof(sabuf));
|
|
|
|
snprintf(a->saddr, sizeof(a->saddr), "%s/%d", sa, a->prefix_len);
|
2013-04-01 20:15:47 +08:00
|
|
|
TAILQ_INSERT_TAIL(&state->addrs, a, next);
|
|
|
|
return a;
|
|
|
|
}
|
|
|
|
|
2014-09-27 06:35:46 +08:00
|
|
|
static void
|
2014-10-17 20:37:44 +08:00
|
|
|
dhcp6_script_try_run(struct interface *ifp, int delegated)
|
2014-09-27 06:35:46 +08:00
|
|
|
{
|
|
|
|
struct dhcp6_state *state;
|
|
|
|
struct ipv6_addr *ap;
|
2014-10-17 20:37:44 +08:00
|
|
|
int completed;
|
2014-09-27 06:35:46 +08:00
|
|
|
|
|
|
|
state = D6_STATE(ifp);
|
|
|
|
if (!TAILQ_FIRST(&state->addrs))
|
|
|
|
return;
|
|
|
|
|
|
|
|
completed = 1;
|
|
|
|
/* If all addresses have completed DAD run the script */
|
|
|
|
TAILQ_FOREACH(ap, &state->addrs, next) {
|
2014-10-10 04:14:15 +08:00
|
|
|
if (!(ap->flags & IPV6_AF_ADDED))
|
|
|
|
continue;
|
2014-09-27 06:35:46 +08:00
|
|
|
if (ap->flags & IPV6_AF_ONLINK) {
|
|
|
|
if (!(ap->flags & IPV6_AF_DADCOMPLETED) &&
|
2014-10-10 02:59:30 +08:00
|
|
|
ipv6_iffindaddr(ap->iface, &ap->addr))
|
2014-09-27 06:35:46 +08:00
|
|
|
ap->flags |= IPV6_AF_DADCOMPLETED;
|
2014-10-17 20:37:44 +08:00
|
|
|
if ((ap->flags & IPV6_AF_DADCOMPLETED) == 0 &&
|
|
|
|
((delegated && ap->delegating_iface) ||
|
|
|
|
(!delegated && !ap->delegating_iface)))
|
|
|
|
{
|
2014-09-27 06:35:46 +08:00
|
|
|
completed = 0;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (completed) {
|
2014-10-17 20:37:44 +08:00
|
|
|
script_runreason(ifp, delegated ? "DELEGATED6" : state->reason);
|
|
|
|
if (!delegated)
|
2014-10-02 21:38:18 +08:00
|
|
|
dhcpcd_daemonise(ifp->ctx);
|
2014-09-27 06:35:46 +08:00
|
|
|
} else
|
|
|
|
syslog(LOG_DEBUG,
|
|
|
|
"%s: waiting for DHCPv6 DAD to complete",
|
|
|
|
ifp->name);
|
|
|
|
}
|
|
|
|
|
2013-04-01 20:15:47 +08:00
|
|
|
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;
|
2014-03-23 03:55:29 +08:00
|
|
|
uint8_t carrier_warned, abrt;
|
2013-04-01 20:15:47 +08:00
|
|
|
|
|
|
|
ifo = ifp->options;
|
|
|
|
state = D6_STATE(ifp);
|
2014-04-28 20:02:12 +08:00
|
|
|
|
|
|
|
/* Try to load configured interfaces for delegation that do not exist */
|
|
|
|
for (i = 0; i < ifo->ia_len; i++) {
|
|
|
|
ia = &ifo->ia[i];
|
|
|
|
for (j = 0; j < ia->sla_len; j++) {
|
|
|
|
sla = &ia->sla[j];
|
|
|
|
for (k = 0; k < i; j++)
|
|
|
|
if (strcmp(sla->ifname, ia->sla[j].ifname) == 0)
|
|
|
|
break;
|
|
|
|
if (j >= i &&
|
|
|
|
if_find(ifp->ctx, sla->ifname) == NULL)
|
|
|
|
{
|
|
|
|
syslog(LOG_INFO,
|
|
|
|
"%s: loading for delegation", sla->ifname);
|
|
|
|
if (dhcpcd_handleinterface(ifp->ctx, 2,
|
|
|
|
sla->ifname) == -1)
|
|
|
|
syslog(LOG_ERR,
|
|
|
|
"%s: interface does not exist"
|
|
|
|
" for delegation",
|
|
|
|
sla->ifname);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-02-12 08:39:46 +08:00
|
|
|
TAILQ_FOREACH(ifd, ifp->ctx->ifaces, next) {
|
2014-07-08 00:42:30 +08:00
|
|
|
if (ifd->options->options & DHCPCD_NOPFXDLG)
|
2014-07-07 22:41:18 +08:00
|
|
|
continue;
|
2013-04-01 20:15:47 +08:00
|
|
|
k = 0;
|
2014-03-23 03:55:29 +08:00
|
|
|
carrier_warned = abrt = 0;
|
2013-04-01 20:15:47 +08:00
|
|
|
TAILQ_FOREACH(ap, &state->addrs, next) {
|
2014-03-21 00:36:03 +08:00
|
|
|
if (!(ap->flags & IPV6_AF_DELEGATEDPFX))
|
|
|
|
continue;
|
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 */
|
2014-10-08 19:53:27 +08:00
|
|
|
if (ifd->carrier != LINK_UP) {
|
2013-06-02 17:51:30 +08:00
|
|
|
syslog(LOG_DEBUG,
|
|
|
|
"%s: has no carrier, cannot"
|
|
|
|
" delegate addresses",
|
|
|
|
ifd->name);
|
|
|
|
carrier_warned = 1;
|
|
|
|
break;
|
|
|
|
}
|
2014-07-05 03:44:15 +08:00
|
|
|
if (dhcp6_ifdelegateaddr(ifd, ap,
|
2014-10-15 22:23:53 +08:00
|
|
|
NULL, ia, 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];
|
2014-07-08 00:42:30 +08:00
|
|
|
if (sla->sla_set && sla->sla == 0)
|
2014-03-23 03:55:29 +08:00
|
|
|
ap->flags |=
|
|
|
|
IPV6_AF_DELEGATEDZERO;
|
2013-04-01 20:15:47 +08:00
|
|
|
if (strcmp(ifd->name, sla->ifname))
|
|
|
|
continue;
|
2014-10-08 19:53:27 +08:00
|
|
|
if (ifd->carrier != LINK_UP) {
|
2013-06-02 05:30:18 +08:00
|
|
|
syslog(LOG_DEBUG,
|
|
|
|
"%s: has no carrier, cannot"
|
|
|
|
" delegate addresses",
|
|
|
|
ifd->name);
|
|
|
|
carrier_warned = 1;
|
|
|
|
break;
|
|
|
|
}
|
2014-07-05 03:44:15 +08:00
|
|
|
if (dhcp6_ifdelegateaddr(ifd, ap,
|
2014-10-15 22:23:53 +08:00
|
|
|
sla, ia, ifp))
|
2013-04-01 20:15:47 +08:00
|
|
|
k++;
|
|
|
|
}
|
2014-03-23 03:55:29 +08:00
|
|
|
if (carrier_warned ||abrt)
|
2013-06-02 05:30:18 +08:00
|
|
|
break;
|
2013-04-01 20:15:47 +08:00
|
|
|
}
|
2014-03-23 03:55:29 +08:00
|
|
|
if (carrier_warned || abrt)
|
2013-06-02 05:30:18 +08:00
|
|
|
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);
|
2014-10-17 20:37:44 +08:00
|
|
|
dhcp6_script_try_run(ifd, 1);
|
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);
|
|
|
|
}
|
|
|
|
|
2014-03-28 06:14:52 +08:00
|
|
|
size_t
|
2013-04-01 20:15:47 +08:00
|
|
|
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;
|
|
|
|
state = D6_STATE(ifd);
|
|
|
|
if (state == NULL || state->state != DH6S_BOUND)
|
|
|
|
continue;
|
|
|
|
TAILQ_FOREACH(ap, &state->addrs, next) {
|
2014-03-21 00:36:03 +08:00
|
|
|
if (!(ap->flags & IPV6_AF_DELEGATEDPFX))
|
|
|
|
continue;
|
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;
|
|
|
|
}
|
2014-07-05 03:44:15 +08:00
|
|
|
if (dhcp6_ifdelegateaddr(ifp, ap,
|
2014-10-15 22:23:53 +08:00
|
|
|
sla, ia, ifd))
|
2013-04-01 20:15:47 +08:00
|
|
|
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);
|
2014-10-17 20:37:44 +08:00
|
|
|
dhcp6_script_try_run(ifp, 1);
|
2013-04-01 20:15:47 +08:00
|
|
|
}
|
|
|
|
return k;
|
|
|
|
}
|
|
|
|
|
2014-07-07 22:41:18 +08:00
|
|
|
static struct interface *
|
|
|
|
dhcp6_findpfxdlgif(struct interface *ifp)
|
|
|
|
{
|
|
|
|
struct interface *ifn;
|
|
|
|
|
|
|
|
if (ifp->options && ifp->options->options & DHCPCD_PFXDLGONLY)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
if (ifp->ctx && ifp->ctx->ifaces) {
|
|
|
|
TAILQ_FOREACH(ifn, ifp->ctx->ifaces, next) {
|
|
|
|
if (strcmp(ifn->name, ifp->name) == 0 &&
|
|
|
|
ifn->options->options & DHCPCD_PFXDLGONLY)
|
|
|
|
return ifn;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
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;
|
2014-03-28 06:14:52 +08:00
|
|
|
size_t i, len;
|
|
|
|
ssize_t bytes;
|
2012-10-12 18:31:51 +08:00
|
|
|
struct cmsghdr *cm;
|
|
|
|
struct in6_pktinfo pkt;
|
2014-07-07 22:41:18 +08:00
|
|
|
struct interface *ifp, *ifpx;
|
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;
|
2014-03-12 23:39:53 +08:00
|
|
|
ctx->rcvhdr.msg_controllen = CMSG_SPACE(sizeof(struct in6_pktinfo));
|
2014-03-28 06:14:52 +08:00
|
|
|
bytes = recvmsg(ctx->dhcp_fd, &ctx->rcvhdr, 0);
|
2014-11-26 04:40:00 +08:00
|
|
|
if (bytes == -1) {
|
2012-10-12 18:31:51 +08:00
|
|
|
syslog(LOG_ERR, "recvmsg: %m");
|
2014-05-03 07:54:29 +08:00
|
|
|
close(ctx->dhcp_fd);
|
2014-09-05 03:30:47 +08:00
|
|
|
eloop_event_delete(dhcpcd_ctx->eloop, ctx->dhcp_fd, 0);
|
2014-05-03 07:54:29 +08:00
|
|
|
ctx->dhcp_fd = -1;
|
2012-10-12 18:31:51 +08:00
|
|
|
return;
|
|
|
|
}
|
2014-03-28 06:14:52 +08:00
|
|
|
len = (size_t)bytes;
|
2014-02-12 08:39:46 +08:00
|
|
|
ctx->sfrom = inet_ntop(AF_INET6, &ctx->from.sin6_addr,
|
|
|
|
ctx->ntopbuf, sizeof(ctx->ntopbuf));
|
2014-03-28 06:14:52 +08:00
|
|
|
if (len < sizeof(struct dhcp6_message)) {
|
2014-11-26 05:08:34 +08:00
|
|
|
syslog(LOG_ERR, "DHCPv6 packet too short from %s",
|
2014-02-12 08:39:46 +08:00
|
|
|
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;
|
|
|
|
}
|
2014-07-07 22:41:18 +08:00
|
|
|
|
|
|
|
r = (struct dhcp6_message *)ctx->rcvhdr.msg_iov[0].iov_base;
|
|
|
|
|
|
|
|
/* Which interface state is the IAID for? */
|
|
|
|
ifpx = dhcp6_findpfxdlgif(ifp);
|
|
|
|
if (ifpx && D6_STATE(ifpx)) {
|
|
|
|
state = D6_STATE(ifpx);
|
|
|
|
if (r->xid[0] == state->send->xid[0] &&
|
|
|
|
r->xid[1] == state->send->xid[1] &&
|
|
|
|
r->xid[2] == state->send->xid[2])
|
|
|
|
ifp = ifpx;
|
|
|
|
}
|
|
|
|
|
2012-10-12 18:31:51 +08:00
|
|
|
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;
|
|
|
|
}
|
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-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-12-17 04:20:01 +08:00
|
|
|
dhcp6_validatelease(ifp, r, len,
|
|
|
|
ctx->sfrom, NULL) == -1)
|
2014-03-10 23:28:49 +08:00
|
|
|
{
|
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-12-17 04:20:01 +08:00
|
|
|
if (dhcp6_validatelease(ifp, r, len,
|
|
|
|
ctx->sfrom, NULL) == -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 */
|
2014-07-02 04:34:19 +08:00
|
|
|
if (dhcp6_hasprefixdelegation(ifp))
|
2013-06-11 16:55:58 +08:00
|
|
|
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) {
|
2014-04-25 21:14:21 +08:00
|
|
|
syslog(LOG_DEBUG, "%s: SOL_MAX_RT %llu -> %d",
|
|
|
|
ifp->name,
|
|
|
|
(unsigned long long)state->sol_max_rt, u32);
|
2014-04-25 21:05:15 +08:00
|
|
|
state->sol_max_rt = (time_t)u32;
|
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
|
|
|
} 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) {
|
2014-04-25 21:14:21 +08:00
|
|
|
syslog(LOG_DEBUG, "%s: INF_MAX_RT %llu -> %d",
|
|
|
|
ifp->name,
|
|
|
|
(unsigned long long)state->inf_max_rt, u32);
|
2014-04-25 21:05:15 +08:00
|
|
|
state->inf_max_rt = (time_t)u32;
|
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
|
|
|
} else
|
|
|
|
syslog(LOG_ERR, "%s: invalid INF_MAX_RT %d",
|
|
|
|
ifp->name, u32);
|
|
|
|
}
|
2014-12-17 04:20:01 +08:00
|
|
|
if (dhcp6_validatelease(ifp, r, len, ctx->sfrom, NULL) == -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) {
|
2014-09-15 16:16:36 +08:00
|
|
|
syslog(LOG_ERR,
|
2014-03-11 17:39:52 +08:00
|
|
|
"%s: unauthenticated %s from %s",
|
|
|
|
ifp->name, op, ctx->sfrom);
|
2014-09-15 16:16:36 +08:00
|
|
|
return;
|
2014-01-31 22:25:18 +08:00
|
|
|
}
|
2014-03-11 17:39:52 +08:00
|
|
|
syslog(LOG_INFO, "%s: %s from %s",
|
|
|
|
ifp->name, op, 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,
|
2014-03-11 17:39:52 +08:00
|
|
|
"%s: not bound, ignoring %s",
|
|
|
|
ifp->name, op);
|
2014-01-31 22:25:18 +08:00
|
|
|
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,
|
2014-03-11 17:39:52 +08:00
|
|
|
"%s: not informed, ignoring %s",
|
|
|
|
ifp->name, op);
|
2014-01-31 22:25:18 +08:00
|
|
|
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,
|
2014-03-11 17:39:52 +08:00
|
|
|
"%s: unsupported %s type %d",
|
|
|
|
ifp->name, op, *D6_COPTION_DATA(o));
|
2014-03-11 01:37:24 +08:00
|
|
|
break;
|
2014-01-31 22:25:18 +08:00
|
|
|
}
|
2014-03-11 01:37:24 +08:00
|
|
|
return;
|
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;
|
2014-09-24 20:23:30 +08:00
|
|
|
else if (state->lowpl != ND6_INFINITE_LIFETIME)
|
2014-03-28 06:14:52 +08:00
|
|
|
state->renew = (uint32_t)(state->lowpl * 0.5);
|
2012-11-07 07:40:15 +08:00
|
|
|
}
|
|
|
|
if (state->rebind == 0) {
|
2013-05-31 21:40:46 +08:00
|
|
|
if (state->expire == ND6_INFINITE_LIFETIME)
|
|
|
|
state->rebind = ND6_INFINITE_LIFETIME;
|
2014-09-24 20:23:30 +08:00
|
|
|
else if (state->lowpl != ND6_INFINITE_LIFETIME)
|
2014-03-28 06:14:52 +08:00
|
|
|
state->rebind = (uint32_t)(state->lowpl * 0.8);
|
2012-11-07 07:40:15 +08:00
|
|
|
}
|
|
|
|
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-04-25 21:05:15 +08:00
|
|
|
eloop_timeout_add_sec(ifp->ctx->eloop,
|
|
|
|
(time_t)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-04-25 21:05:15 +08:00
|
|
|
eloop_timeout_add_sec(ifp->ctx->eloop,
|
|
|
|
(time_t)state->rebind, dhcp6_startrebind, ifp);
|
2014-09-24 20:23:30 +08:00
|
|
|
if (state->expire != ND6_INFINITE_LIFETIME)
|
2014-04-25 21:05:15 +08:00
|
|
|
eloop_timeout_add_sec(ifp->ctx->eloop,
|
|
|
|
(time_t)state->expire, dhcp6_startexpire, ifp);
|
2014-03-03 21:56:49 +08:00
|
|
|
|
2014-03-07 03:11:55 +08:00
|
|
|
ipv6_addaddrs(&state->addrs);
|
2014-07-02 04:34:19 +08:00
|
|
|
dhcp6_delegate_prefix(ifp);
|
|
|
|
|
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-09-24 20:23:30 +08:00
|
|
|
else if (state->expire == 0)
|
|
|
|
syslog(has_new ? LOG_INFO : LOG_DEBUG,
|
|
|
|
"%s: will expire", ifp->name);
|
2014-02-12 08:39:46 +08:00
|
|
|
ipv6_buildroutes(ifp->ctx);
|
2012-11-07 07:40:15 +08:00
|
|
|
dhcp6_writelease(ifp);
|
2014-10-17 20:37:44 +08:00
|
|
|
dhcp6_script_try_run(ifp, 0);
|
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)
|
2014-06-03 22:03:14 +08:00
|
|
|
syslog(LOG_WARNING, "setsockopt: SO_REUSEPORT: %m");
|
2012-10-12 18:31:51 +08:00
|
|
|
#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)
|
2014-02-12 08:39:46 +08:00
|
|
|
goto errexit;
|
|
|
|
|
2014-09-05 03:30:47 +08:00
|
|
|
eloop_event_add(dctx->eloop, ctx->dhcp_fd,
|
|
|
|
dhcp6_handledata, dctx, NULL, NULL);
|
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);
|
|
|
|
}
|
|
|
|
|
2014-07-05 03:44:15 +08:00
|
|
|
/* Rapid commit won't wor with Prefix Delegation Exclusion */
|
|
|
|
if (dhcp6_findselfsla(ifp, NULL))
|
|
|
|
del_option_mask(ifo->requestmask6, D6_OPTION_RAPID_COMMIT);
|
|
|
|
|
2014-07-07 22:41:18 +08:00
|
|
|
/* Create a 2nd interface to handle the PD state */
|
|
|
|
if (!(ifo->options & (DHCPCD_PFXDLGONLY | DHCPCD_PFXDLGMIX)) &&
|
2014-07-09 12:41:23 +08:00
|
|
|
dhcp6_hasprefixdelegation(ifp) > 1)
|
2014-07-07 22:41:18 +08:00
|
|
|
{
|
|
|
|
const char * const argv[] = { ifp->name };
|
|
|
|
struct if_head *ifs;
|
|
|
|
struct interface *ifn;
|
|
|
|
|
|
|
|
ifn = dhcp6_findpfxdlgif(ifp);
|
|
|
|
if (ifn == NULL) {
|
|
|
|
ifs = if_discover(ifp->ctx, -1, UNCONST(argv));
|
|
|
|
if (ifs) {
|
|
|
|
ifn = TAILQ_FIRST(ifs);
|
|
|
|
if (ifn) {
|
|
|
|
syslog(LOG_INFO,
|
2014-07-07 22:44:06 +08:00
|
|
|
"%s: creating pseudo interface"
|
2014-07-07 22:41:18 +08:00
|
|
|
" to handle Prefix Delegation",
|
|
|
|
ifp->name);
|
|
|
|
ifp->options->options |=
|
|
|
|
DHCPCD_NOPFXDLG;
|
|
|
|
TAILQ_REMOVE(ifs, ifn, next);
|
|
|
|
TAILQ_INSERT_AFTER(ifp->ctx->ifaces,
|
|
|
|
ifp, ifn, next);
|
|
|
|
dhcpcd_initstate(ifn);
|
|
|
|
ifn->options->options |=
|
|
|
|
DHCPCD_PFXDLGONLY;
|
|
|
|
ifn->options->options &=
|
|
|
|
~(DHCPCD_IPV4 | DHCPCD_IPV6RS |
|
|
|
|
DHCPCD_NOPFXDLG);
|
|
|
|
eloop_timeout_add_sec(ifp->ctx->eloop,
|
|
|
|
0, dhcpcd_startinterface, ifn);
|
|
|
|
}
|
|
|
|
while ((ifn = TAILQ_FIRST(ifs))) {
|
|
|
|
TAILQ_REMOVE(ifs, ifn, next);
|
|
|
|
if_free(ifn);
|
|
|
|
}
|
|
|
|
free(ifs);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
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-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;
|
|
|
|
}
|
2014-10-17 21:47:05 +08:00
|
|
|
if (init_state == DH6S_INIT &&
|
|
|
|
ifp->options->options & DHCPCD_DHCP6 &&
|
|
|
|
(state->state == DH6S_INFORM ||
|
|
|
|
state->state == DH6S_INFORMED ||
|
|
|
|
state->state == DH6S_DELEGATED))
|
|
|
|
{
|
|
|
|
/* Change from stateless to stateful */
|
|
|
|
goto gogogo;
|
|
|
|
}
|
2012-10-12 18:31:51 +08:00
|
|
|
/* We're already running DHCP6 */
|
2014-10-17 21:47:05 +08:00
|
|
|
/* XXX: What if the managed flag vanishes from all RA? */
|
2012-10-12 18:31:51 +08:00
|
|
|
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-04-01 20:15:47 +08:00
|
|
|
|
2014-10-17 21:47:05 +08:00
|
|
|
gogogo:
|
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),
|
2014-07-04 19:53:56 +08:00
|
|
|
LEASEFILE6, ifp->name,
|
|
|
|
ifp->options->options & DHCPCD_PFXDLGONLY ? ".pd" : "");
|
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
|
|
|
{
|
2014-07-07 22:41:18 +08:00
|
|
|
struct interface *ifpx;
|
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;
|
2014-10-02 19:07:00 +08:00
|
|
|
int dropdele;
|
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-10-02 19:07:00 +08:00
|
|
|
if (ifp->options)
|
|
|
|
options = ifp->options->options;
|
|
|
|
else
|
|
|
|
options = 0;
|
|
|
|
dropdele = (options & (DHCPCD_STOPPING | DHCPCD_RELEASE) &&
|
2014-02-28 04:00:39 +08:00
|
|
|
(options &
|
|
|
|
(DHCPCD_EXITING | DHCPCD_PERSISTENT)) !=
|
2014-10-02 19:07:00 +08:00
|
|
|
(DHCPCD_EXITING | DHCPCD_PERSISTENT));
|
|
|
|
|
|
|
|
ifpx = dhcp6_findpfxdlgif(ifp);
|
|
|
|
if (ifpx) {
|
2014-10-08 20:25:15 +08:00
|
|
|
if (options & DHCPCD_EXITING)
|
|
|
|
ifpx->options->options |= DHCPCD_EXITING;
|
2014-10-02 19:07:00 +08:00
|
|
|
dhcp6_freedrop(ifpx, dropdele ? 1 : drop, reason);
|
|
|
|
TAILQ_REMOVE(ifp->ctx->ifaces, ifpx, next);
|
|
|
|
if_free(ifpx);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ifp->ctx->eloop)
|
|
|
|
eloop_timeout_delete(ifp->ctx->eloop, NULL, ifp);
|
|
|
|
|
|
|
|
if (dropdele)
|
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) {
|
2014-10-08 19:53:27 +08:00
|
|
|
if (ifp->carrier == LINK_UP)
|
2013-04-05 07:30:14 +08:00
|
|
|
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);
|
2014-05-06 22:39:31 +08:00
|
|
|
free(state->old);
|
|
|
|
state->old = state->new;
|
2014-07-09 03:28:52 +08:00
|
|
|
state->old_len = state->new_len;
|
2014-05-06 22:39:31 +08:00
|
|
|
state->new = NULL;
|
2014-07-09 03:28:52 +08:00
|
|
|
state->new_len = 0;
|
2014-05-06 22:39:31 +08:00
|
|
|
if (drop && state->old &&
|
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
|
|
|
}
|
2014-05-06 22:39:31 +08:00
|
|
|
free(state->old);
|
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);
|
|
|
|
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) {
|
2014-09-05 03:30:47 +08:00
|
|
|
eloop_event_delete(ctx->eloop, ctx->ipv6->dhcp_fd, 0);
|
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,
|
2014-03-28 06:14:52 +08:00
|
|
|
const struct dhcp6_message *m, size_t len)
|
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-07-02 04:34:19 +08:00
|
|
|
size_t i, n;
|
2013-12-04 01:55:40 +08:00
|
|
|
uint16_t ol, oc;
|
2014-07-02 04:34:19 +08:00
|
|
|
char *pfx;
|
2013-12-07 01:47:53 +08:00
|
|
|
uint32_t en;
|
2014-02-12 08:39:46 +08:00
|
|
|
const struct dhcpcd_ctx *ctx;
|
2014-09-27 05:22:14 +08:00
|
|
|
const struct dhcp6_state *state;
|
|
|
|
const struct ipv6_addr *ap;
|
|
|
|
char *v, *val;
|
|
|
|
|
|
|
|
n = 0;
|
|
|
|
if (m == NULL)
|
|
|
|
goto delegated;
|
2012-10-12 18:31:51 +08:00
|
|
|
|
2014-07-02 04:34:19 +08:00
|
|
|
if (len < sizeof(*m)) {
|
2014-07-09 03:28:52 +08:00
|
|
|
/* Should be impossible with guards at packet in
|
|
|
|
* and reading leases */
|
2014-07-02 04:34:19 +08:00
|
|
|
errno = EINVAL;
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
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__);
|
2014-03-28 06:14:52 +08:00
|
|
|
return -1;
|
2013-12-04 01:55:40 +08:00
|
|
|
}
|
|
|
|
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);
|
2014-03-28 06:14:52 +08:00
|
|
|
if (sizeof(*o) + ol > len) {
|
|
|
|
errno = EINVAL;
|
2013-12-04 01:55:40 +08:00
|
|
|
break;
|
|
|
|
}
|
2014-03-28 06:14:52 +08:00
|
|
|
len -= sizeof(*o) + ol;
|
2013-12-04 01:55:40 +08:00
|
|
|
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
|
|
|
|
2014-09-27 05:22:14 +08:00
|
|
|
delegated:
|
|
|
|
/* Needed for Delegated Prefixes */
|
|
|
|
state = D6_CSTATE(ifp);
|
|
|
|
i = 0;
|
|
|
|
TAILQ_FOREACH(ap, &state->addrs, next) {
|
|
|
|
if (ap->delegating_iface) {
|
2014-10-02 18:07:59 +08:00
|
|
|
i += strlen(ap->saddr) + 1;
|
2014-09-27 05:22:14 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
if (env && i) {
|
2014-10-04 01:54:06 +08:00
|
|
|
i += strlen(prefix) + strlen("_delegated_dhcp6_prefix=");
|
2014-09-27 05:22:14 +08:00
|
|
|
v = val = env[n] = malloc(i);
|
|
|
|
if (v == NULL) {
|
|
|
|
syslog(LOG_ERR, "%s: %m", __func__);
|
|
|
|
return -1;
|
|
|
|
}
|
2014-10-04 01:54:06 +08:00
|
|
|
v += snprintf(val, i, "%s_delegated_dhcp6_prefix=", prefix);
|
2014-09-27 05:22:14 +08:00
|
|
|
TAILQ_FOREACH(ap, &state->addrs, next) {
|
|
|
|
if (ap->delegating_iface) {
|
2014-10-02 18:07:59 +08:00
|
|
|
/* Can't use stpcpy(3) due to "security" */
|
|
|
|
const char *sap = ap->saddr;
|
|
|
|
|
|
|
|
do
|
|
|
|
*v++ = *sap;
|
2014-10-02 18:51:32 +08:00
|
|
|
while (*++sap != '\0');
|
2014-09-27 05:22:14 +08:00
|
|
|
*v++ = ' ';
|
|
|
|
}
|
|
|
|
}
|
|
|
|
*--v = '\0';
|
|
|
|
}
|
|
|
|
if (i)
|
|
|
|
n++;
|
|
|
|
|
2014-03-28 06:14:52 +08:00
|
|
|
return (ssize_t)n;
|
2012-10-12 18:31:51 +08:00
|
|
|
}
|
2014-06-22 21:05:05 +08:00
|
|
|
|
|
|
|
int
|
|
|
|
dhcp6_dump(struct interface *ifp)
|
|
|
|
{
|
|
|
|
struct dhcp6_state *state;
|
|
|
|
int r;
|
|
|
|
|
|
|
|
ifp->if_data[IF_DATA_DHCP6] = state = calloc(1, sizeof(*state));
|
|
|
|
if (state == NULL)
|
|
|
|
goto eexit;
|
|
|
|
TAILQ_INIT(&state->addrs);
|
|
|
|
snprintf(state->leasefile, sizeof(state->leasefile),
|
2014-07-04 19:53:56 +08:00
|
|
|
LEASEFILE6, ifp->name,
|
|
|
|
ifp->options->options & DHCPCD_PFXDLGONLY ? ".pd" : "");
|
2014-06-22 21:05:05 +08:00
|
|
|
r = dhcp6_readlease(ifp);
|
|
|
|
if (r == -1 && errno == ENOENT) {
|
|
|
|
strlcpy(state->leasefile, ifp->name, sizeof(state->leasefile));
|
|
|
|
r = dhcp6_readlease(ifp);
|
|
|
|
}
|
|
|
|
if (r == -1) {
|
|
|
|
if (errno == ENOENT)
|
|
|
|
syslog(LOG_ERR, "%s: no lease to dump", ifp->name);
|
|
|
|
else
|
|
|
|
syslog(LOG_ERR, "%s: dhcp6_readlease: %m", ifp->name);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
state->reason = "DUMP6";
|
|
|
|
return script_runreason(ifp, state->reason);
|
|
|
|
|
|
|
|
eexit:
|
|
|
|
syslog(LOG_ERR, "%s: %m", __func__);
|
|
|
|
return -1;
|
|
|
|
}
|