1*9f20bfa6SDavid van Moolenbroek #include <sys/cdefs.h>
2*9f20bfa6SDavid van Moolenbroek __RCSID("$NetBSD: dhcp6.c,v 1.15 2015/08/21 10:39:00 roy Exp $");
3*9f20bfa6SDavid van Moolenbroek
4*9f20bfa6SDavid van Moolenbroek /*
5*9f20bfa6SDavid van Moolenbroek * dhcpcd - DHCP client daemon
6*9f20bfa6SDavid van Moolenbroek * Copyright (c) 2006-2015 Roy Marples <roy@marples.name>
7*9f20bfa6SDavid van Moolenbroek * All rights reserved
8*9f20bfa6SDavid van Moolenbroek
9*9f20bfa6SDavid van Moolenbroek * Redistribution and use in source and binary forms, with or without
10*9f20bfa6SDavid van Moolenbroek * modification, are permitted provided that the following conditions
11*9f20bfa6SDavid van Moolenbroek * are met:
12*9f20bfa6SDavid van Moolenbroek * 1. Redistributions of source code must retain the above copyright
13*9f20bfa6SDavid van Moolenbroek * notice, this list of conditions and the following disclaimer.
14*9f20bfa6SDavid van Moolenbroek * 2. Redistributions in binary form must reproduce the above copyright
15*9f20bfa6SDavid van Moolenbroek * notice, this list of conditions and the following disclaimer in the
16*9f20bfa6SDavid van Moolenbroek * documentation and/or other materials provided with the distribution.
17*9f20bfa6SDavid van Moolenbroek *
18*9f20bfa6SDavid van Moolenbroek * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
19*9f20bfa6SDavid van Moolenbroek * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20*9f20bfa6SDavid van Moolenbroek * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21*9f20bfa6SDavid van Moolenbroek * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
22*9f20bfa6SDavid van Moolenbroek * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23*9f20bfa6SDavid van Moolenbroek * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24*9f20bfa6SDavid van Moolenbroek * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25*9f20bfa6SDavid van Moolenbroek * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26*9f20bfa6SDavid van Moolenbroek * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27*9f20bfa6SDavid van Moolenbroek * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28*9f20bfa6SDavid van Moolenbroek * SUCH DAMAGE.
29*9f20bfa6SDavid van Moolenbroek */
30*9f20bfa6SDavid van Moolenbroek
31*9f20bfa6SDavid van Moolenbroek /* TODO: We should decline dupliate addresses detected */
32*9f20bfa6SDavid van Moolenbroek
33*9f20bfa6SDavid van Moolenbroek #include <sys/stat.h>
34*9f20bfa6SDavid van Moolenbroek #include <sys/utsname.h>
35*9f20bfa6SDavid van Moolenbroek
36*9f20bfa6SDavid van Moolenbroek #include <netinet/in.h>
37*9f20bfa6SDavid van Moolenbroek
38*9f20bfa6SDavid van Moolenbroek #include <assert.h>
39*9f20bfa6SDavid van Moolenbroek #include <ctype.h>
40*9f20bfa6SDavid van Moolenbroek #include <errno.h>
41*9f20bfa6SDavid van Moolenbroek #include <fcntl.h>
42*9f20bfa6SDavid van Moolenbroek #include <inttypes.h>
43*9f20bfa6SDavid van Moolenbroek #include <stdlib.h>
44*9f20bfa6SDavid van Moolenbroek #include <string.h>
45*9f20bfa6SDavid van Moolenbroek #include <unistd.h>
46*9f20bfa6SDavid van Moolenbroek #include <fcntl.h>
47*9f20bfa6SDavid van Moolenbroek
48*9f20bfa6SDavid van Moolenbroek #define ELOOP_QUEUE 4
49*9f20bfa6SDavid van Moolenbroek #include "config.h"
50*9f20bfa6SDavid van Moolenbroek #include "common.h"
51*9f20bfa6SDavid van Moolenbroek #include "dhcp.h"
52*9f20bfa6SDavid van Moolenbroek #include "dhcp6.h"
53*9f20bfa6SDavid van Moolenbroek #include "duid.h"
54*9f20bfa6SDavid van Moolenbroek #include "eloop.h"
55*9f20bfa6SDavid van Moolenbroek #include "if.h"
56*9f20bfa6SDavid van Moolenbroek #include "if-options.h"
57*9f20bfa6SDavid van Moolenbroek #include "ipv6nd.h"
58*9f20bfa6SDavid van Moolenbroek #include "script.h"
59*9f20bfa6SDavid van Moolenbroek
60*9f20bfa6SDavid van Moolenbroek #ifndef __UNCONST
61*9f20bfa6SDavid van Moolenbroek #define __UNCONST(a) ((void *)(unsigned long)(const void *)(a))
62*9f20bfa6SDavid van Moolenbroek #endif
63*9f20bfa6SDavid van Moolenbroek
64*9f20bfa6SDavid van Moolenbroek /* DHCPCD Project has been assigned an IANA PEN of 40712 */
65*9f20bfa6SDavid van Moolenbroek #define DHCPCD_IANA_PEN 40712
66*9f20bfa6SDavid van Moolenbroek
67*9f20bfa6SDavid van Moolenbroek /* Unsure if I want this */
68*9f20bfa6SDavid van Moolenbroek //#define VENDOR_SPLIT
69*9f20bfa6SDavid van Moolenbroek
70*9f20bfa6SDavid van Moolenbroek /* Support older systems with different defines */
71*9f20bfa6SDavid van Moolenbroek #if !defined(IPV6_RECVPKTINFO) && defined(IPV6_PKTINFO)
72*9f20bfa6SDavid van Moolenbroek #define IPV6_RECVPKTINFO IPV6_PKTINFO
73*9f20bfa6SDavid van Moolenbroek #endif
74*9f20bfa6SDavid van Moolenbroek
75*9f20bfa6SDavid van Moolenbroek struct dhcp6_op {
76*9f20bfa6SDavid van Moolenbroek uint16_t type;
77*9f20bfa6SDavid van Moolenbroek const char *name;
78*9f20bfa6SDavid van Moolenbroek };
79*9f20bfa6SDavid van Moolenbroek
80*9f20bfa6SDavid van Moolenbroek static const struct dhcp6_op dhcp6_ops[] = {
81*9f20bfa6SDavid van Moolenbroek { DHCP6_SOLICIT, "SOLICIT6" },
82*9f20bfa6SDavid van Moolenbroek { DHCP6_ADVERTISE, "ADVERTISE6" },
83*9f20bfa6SDavid van Moolenbroek { DHCP6_REQUEST, "REQUEST6" },
84*9f20bfa6SDavid van Moolenbroek { DHCP6_REPLY, "REPLY6" },
85*9f20bfa6SDavid van Moolenbroek { DHCP6_RENEW, "RENEW6" },
86*9f20bfa6SDavid van Moolenbroek { DHCP6_REBIND, "REBIND6" },
87*9f20bfa6SDavid van Moolenbroek { DHCP6_CONFIRM, "CONFIRM6" },
88*9f20bfa6SDavid van Moolenbroek { DHCP6_INFORMATION_REQ, "INFORM6" },
89*9f20bfa6SDavid van Moolenbroek { DHCP6_RELEASE, "RELEASE6" },
90*9f20bfa6SDavid van Moolenbroek { DHCP6_RECONFIGURE, "RECONFIURE6" },
91*9f20bfa6SDavid van Moolenbroek { 0, NULL }
92*9f20bfa6SDavid van Moolenbroek };
93*9f20bfa6SDavid van Moolenbroek
94*9f20bfa6SDavid van Moolenbroek struct dhcp_compat {
95*9f20bfa6SDavid van Moolenbroek uint8_t dhcp_opt;
96*9f20bfa6SDavid van Moolenbroek uint16_t dhcp6_opt;
97*9f20bfa6SDavid van Moolenbroek };
98*9f20bfa6SDavid van Moolenbroek
99*9f20bfa6SDavid van Moolenbroek const struct dhcp_compat dhcp_compats[] = {
100*9f20bfa6SDavid van Moolenbroek { DHO_DNSSERVER, D6_OPTION_DNS_SERVERS },
101*9f20bfa6SDavid van Moolenbroek { DHO_HOSTNAME, D6_OPTION_FQDN },
102*9f20bfa6SDavid van Moolenbroek { DHO_DNSDOMAIN, D6_OPTION_FQDN },
103*9f20bfa6SDavid van Moolenbroek { DHO_NISSERVER, D6_OPTION_NIS_SERVERS },
104*9f20bfa6SDavid van Moolenbroek { DHO_NTPSERVER, D6_OPTION_SNTP_SERVERS },
105*9f20bfa6SDavid van Moolenbroek { DHO_RAPIDCOMMIT, D6_OPTION_RAPID_COMMIT },
106*9f20bfa6SDavid van Moolenbroek { DHO_FQDN, D6_OPTION_FQDN },
107*9f20bfa6SDavid van Moolenbroek { DHO_VIVCO, D6_OPTION_VENDOR_CLASS },
108*9f20bfa6SDavid van Moolenbroek { DHO_VIVSO, D6_OPTION_VENDOR_OPTS },
109*9f20bfa6SDavid van Moolenbroek { DHO_DNSSEARCH, D6_OPTION_DOMAIN_LIST },
110*9f20bfa6SDavid van Moolenbroek { 0, 0 }
111*9f20bfa6SDavid van Moolenbroek };
112*9f20bfa6SDavid van Moolenbroek
113*9f20bfa6SDavid van Moolenbroek static const char * const dhcp6_statuses[] = {
114*9f20bfa6SDavid van Moolenbroek "Success",
115*9f20bfa6SDavid van Moolenbroek "Unspecified Failure",
116*9f20bfa6SDavid van Moolenbroek "No Addresses Available",
117*9f20bfa6SDavid van Moolenbroek "No Binding",
118*9f20bfa6SDavid van Moolenbroek "Not On Link",
119*9f20bfa6SDavid van Moolenbroek "Use Multicast"
120*9f20bfa6SDavid van Moolenbroek };
121*9f20bfa6SDavid van Moolenbroek
122*9f20bfa6SDavid van Moolenbroek struct dhcp6_ia_addr {
123*9f20bfa6SDavid van Moolenbroek struct in6_addr addr;
124*9f20bfa6SDavid van Moolenbroek uint32_t pltime;
125*9f20bfa6SDavid van Moolenbroek uint32_t vltime;
126*9f20bfa6SDavid van Moolenbroek } __packed;
127*9f20bfa6SDavid van Moolenbroek
128*9f20bfa6SDavid van Moolenbroek struct dhcp6_pd_addr {
129*9f20bfa6SDavid van Moolenbroek uint32_t pltime;
130*9f20bfa6SDavid van Moolenbroek uint32_t vltime;
131*9f20bfa6SDavid van Moolenbroek uint8_t prefix_len;
132*9f20bfa6SDavid van Moolenbroek struct in6_addr prefix;
133*9f20bfa6SDavid van Moolenbroek } __packed;
134*9f20bfa6SDavid van Moolenbroek
135*9f20bfa6SDavid van Moolenbroek void
dhcp6_printoptions(const struct dhcpcd_ctx * ctx,const struct dhcp_opt * opts,size_t opts_len)136*9f20bfa6SDavid van Moolenbroek dhcp6_printoptions(const struct dhcpcd_ctx *ctx,
137*9f20bfa6SDavid van Moolenbroek const struct dhcp_opt *opts, size_t opts_len)
138*9f20bfa6SDavid van Moolenbroek {
139*9f20bfa6SDavid van Moolenbroek size_t i, j;
140*9f20bfa6SDavid van Moolenbroek const struct dhcp_opt *opt, *opt2;
141*9f20bfa6SDavid van Moolenbroek int cols;
142*9f20bfa6SDavid van Moolenbroek
143*9f20bfa6SDavid van Moolenbroek for (i = 0, opt = ctx->dhcp6_opts;
144*9f20bfa6SDavid van Moolenbroek i < ctx->dhcp6_opts_len; i++, opt++)
145*9f20bfa6SDavid van Moolenbroek {
146*9f20bfa6SDavid van Moolenbroek for (j = 0, opt2 = opts; j < opts_len; j++, opt2++)
147*9f20bfa6SDavid van Moolenbroek if (opt2->option == opt->option)
148*9f20bfa6SDavid van Moolenbroek break;
149*9f20bfa6SDavid van Moolenbroek if (j == opts_len) {
150*9f20bfa6SDavid van Moolenbroek cols = printf("%05d %s", opt->option, opt->var);
151*9f20bfa6SDavid van Moolenbroek dhcp_print_option_encoding(opt, cols);
152*9f20bfa6SDavid van Moolenbroek }
153*9f20bfa6SDavid van Moolenbroek }
154*9f20bfa6SDavid van Moolenbroek for (i = 0, opt = opts; i < opts_len; i++, opt++) {
155*9f20bfa6SDavid van Moolenbroek cols = printf("%05d %s", opt->option, opt->var);
156*9f20bfa6SDavid van Moolenbroek dhcp_print_option_encoding(opt, cols);
157*9f20bfa6SDavid van Moolenbroek }
158*9f20bfa6SDavid van Moolenbroek }
159*9f20bfa6SDavid van Moolenbroek
160*9f20bfa6SDavid van Moolenbroek static size_t
dhcp6_makevendor(struct dhcp6_option * o,const struct interface * ifp)161*9f20bfa6SDavid van Moolenbroek dhcp6_makevendor(struct dhcp6_option *o, const struct interface *ifp)
162*9f20bfa6SDavid van Moolenbroek {
163*9f20bfa6SDavid van Moolenbroek const struct if_options *ifo;
164*9f20bfa6SDavid van Moolenbroek size_t len, i;
165*9f20bfa6SDavid van Moolenbroek uint8_t *p;
166*9f20bfa6SDavid van Moolenbroek uint16_t u16;
167*9f20bfa6SDavid van Moolenbroek uint32_t u32;
168*9f20bfa6SDavid van Moolenbroek ssize_t vlen;
169*9f20bfa6SDavid van Moolenbroek const struct vivco *vivco;
170*9f20bfa6SDavid van Moolenbroek char vendor[VENDORCLASSID_MAX_LEN];
171*9f20bfa6SDavid van Moolenbroek
172*9f20bfa6SDavid van Moolenbroek ifo = ifp->options;
173*9f20bfa6SDavid van Moolenbroek len = sizeof(uint32_t); /* IANA PEN */
174*9f20bfa6SDavid van Moolenbroek if (ifo->vivco_en) {
175*9f20bfa6SDavid van Moolenbroek for (i = 0, vivco = ifo->vivco;
176*9f20bfa6SDavid van Moolenbroek i < ifo->vivco_len;
177*9f20bfa6SDavid van Moolenbroek i++, vivco++)
178*9f20bfa6SDavid van Moolenbroek len += sizeof(uint16_t) + vivco->len;
179*9f20bfa6SDavid van Moolenbroek vlen = 0; /* silence bogus gcc warning */
180*9f20bfa6SDavid van Moolenbroek } else {
181*9f20bfa6SDavid van Moolenbroek vlen = dhcp_vendor(vendor, sizeof(vendor));
182*9f20bfa6SDavid van Moolenbroek if (vlen == -1)
183*9f20bfa6SDavid van Moolenbroek vlen = 0;
184*9f20bfa6SDavid van Moolenbroek else
185*9f20bfa6SDavid van Moolenbroek len += sizeof(uint16_t) + (size_t)vlen;
186*9f20bfa6SDavid van Moolenbroek }
187*9f20bfa6SDavid van Moolenbroek
188*9f20bfa6SDavid van Moolenbroek if (len > UINT16_MAX) {
189*9f20bfa6SDavid van Moolenbroek logger(ifp->ctx, LOG_ERR,
190*9f20bfa6SDavid van Moolenbroek "%s: DHCPv6 Vendor Class too big", ifp->name);
191*9f20bfa6SDavid van Moolenbroek return 0;
192*9f20bfa6SDavid van Moolenbroek }
193*9f20bfa6SDavid van Moolenbroek
194*9f20bfa6SDavid van Moolenbroek if (o) {
195*9f20bfa6SDavid van Moolenbroek o->code = htons(D6_OPTION_VENDOR_CLASS);
196*9f20bfa6SDavid van Moolenbroek o->len = htons((uint16_t)len);
197*9f20bfa6SDavid van Moolenbroek p = D6_OPTION_DATA(o);
198*9f20bfa6SDavid van Moolenbroek u32 = htonl(ifo->vivco_en ? ifo->vivco_en : DHCPCD_IANA_PEN);
199*9f20bfa6SDavid van Moolenbroek memcpy(p, &u32, sizeof(u32));
200*9f20bfa6SDavid van Moolenbroek p += sizeof(u32);
201*9f20bfa6SDavid van Moolenbroek if (ifo->vivco_en) {
202*9f20bfa6SDavid van Moolenbroek for (i = 0, vivco = ifo->vivco;
203*9f20bfa6SDavid van Moolenbroek i < ifo->vivco_len;
204*9f20bfa6SDavid van Moolenbroek i++, vivco++)
205*9f20bfa6SDavid van Moolenbroek {
206*9f20bfa6SDavid van Moolenbroek u16 = htons((uint16_t)vivco->len);
207*9f20bfa6SDavid van Moolenbroek memcpy(p, &u16, sizeof(u16));
208*9f20bfa6SDavid van Moolenbroek p += sizeof(u16);
209*9f20bfa6SDavid van Moolenbroek memcpy(p, vivco->data, vivco->len);
210*9f20bfa6SDavid van Moolenbroek p += vivco->len;
211*9f20bfa6SDavid van Moolenbroek }
212*9f20bfa6SDavid van Moolenbroek } else if (vlen) {
213*9f20bfa6SDavid van Moolenbroek u16 = htons((uint16_t)vlen);
214*9f20bfa6SDavid van Moolenbroek memcpy(p, &u16, sizeof(u16));
215*9f20bfa6SDavid van Moolenbroek p += sizeof(u16);
216*9f20bfa6SDavid van Moolenbroek memcpy(p, vendor, (size_t)vlen);
217*9f20bfa6SDavid van Moolenbroek }
218*9f20bfa6SDavid van Moolenbroek }
219*9f20bfa6SDavid van Moolenbroek
220*9f20bfa6SDavid van Moolenbroek return len;
221*9f20bfa6SDavid van Moolenbroek }
222*9f20bfa6SDavid van Moolenbroek
223*9f20bfa6SDavid van Moolenbroek static const struct dhcp6_option *
dhcp6_findoption(uint16_t code,const uint8_t * d,size_t len)224*9f20bfa6SDavid van Moolenbroek dhcp6_findoption(uint16_t code, const uint8_t *d, size_t len)
225*9f20bfa6SDavid van Moolenbroek {
226*9f20bfa6SDavid van Moolenbroek const struct dhcp6_option *o;
227*9f20bfa6SDavid van Moolenbroek size_t ol;
228*9f20bfa6SDavid van Moolenbroek
229*9f20bfa6SDavid van Moolenbroek code = htons(code);
230*9f20bfa6SDavid van Moolenbroek for (o = (const struct dhcp6_option *)d;
231*9f20bfa6SDavid van Moolenbroek len >= sizeof(*o);
232*9f20bfa6SDavid van Moolenbroek o = D6_CNEXT_OPTION(o))
233*9f20bfa6SDavid van Moolenbroek {
234*9f20bfa6SDavid van Moolenbroek ol = sizeof(*o) + ntohs(o->len);
235*9f20bfa6SDavid van Moolenbroek if (ol > len) {
236*9f20bfa6SDavid van Moolenbroek errno = EINVAL;
237*9f20bfa6SDavid van Moolenbroek return NULL;
238*9f20bfa6SDavid van Moolenbroek }
239*9f20bfa6SDavid van Moolenbroek if (o->code == code)
240*9f20bfa6SDavid van Moolenbroek return o;
241*9f20bfa6SDavid van Moolenbroek len -= ol;
242*9f20bfa6SDavid van Moolenbroek }
243*9f20bfa6SDavid van Moolenbroek
244*9f20bfa6SDavid van Moolenbroek errno = ESRCH;
245*9f20bfa6SDavid van Moolenbroek return NULL;
246*9f20bfa6SDavid van Moolenbroek }
247*9f20bfa6SDavid van Moolenbroek
248*9f20bfa6SDavid van Moolenbroek static const uint8_t *
dhcp6_getoption(struct dhcpcd_ctx * ctx,size_t * os,unsigned int * code,size_t * len,const uint8_t * od,size_t ol,struct dhcp_opt ** oopt)249*9f20bfa6SDavid van Moolenbroek dhcp6_getoption(struct dhcpcd_ctx *ctx,
250*9f20bfa6SDavid van Moolenbroek size_t *os, unsigned int *code, size_t *len,
251*9f20bfa6SDavid van Moolenbroek const uint8_t *od, size_t ol, struct dhcp_opt **oopt)
252*9f20bfa6SDavid van Moolenbroek {
253*9f20bfa6SDavid van Moolenbroek const struct dhcp6_option *o;
254*9f20bfa6SDavid van Moolenbroek size_t i;
255*9f20bfa6SDavid van Moolenbroek struct dhcp_opt *opt;
256*9f20bfa6SDavid van Moolenbroek
257*9f20bfa6SDavid van Moolenbroek if (od) {
258*9f20bfa6SDavid van Moolenbroek *os = sizeof(*o);
259*9f20bfa6SDavid van Moolenbroek if (ol < *os) {
260*9f20bfa6SDavid van Moolenbroek errno = EINVAL;
261*9f20bfa6SDavid van Moolenbroek return NULL;
262*9f20bfa6SDavid van Moolenbroek }
263*9f20bfa6SDavid van Moolenbroek o = (const struct dhcp6_option *)od;
264*9f20bfa6SDavid van Moolenbroek *len = ntohs(o->len);
265*9f20bfa6SDavid van Moolenbroek if (*len > ol) {
266*9f20bfa6SDavid van Moolenbroek errno = EINVAL;
267*9f20bfa6SDavid van Moolenbroek return NULL;
268*9f20bfa6SDavid van Moolenbroek }
269*9f20bfa6SDavid van Moolenbroek *code = ntohs(o->code);
270*9f20bfa6SDavid van Moolenbroek } else
271*9f20bfa6SDavid van Moolenbroek o = NULL;
272*9f20bfa6SDavid van Moolenbroek
273*9f20bfa6SDavid van Moolenbroek for (i = 0, opt = ctx->dhcp6_opts;
274*9f20bfa6SDavid van Moolenbroek i < ctx->dhcp6_opts_len; i++, opt++)
275*9f20bfa6SDavid van Moolenbroek {
276*9f20bfa6SDavid van Moolenbroek if (opt->option == *code) {
277*9f20bfa6SDavid van Moolenbroek *oopt = opt;
278*9f20bfa6SDavid van Moolenbroek break;
279*9f20bfa6SDavid van Moolenbroek }
280*9f20bfa6SDavid van Moolenbroek }
281*9f20bfa6SDavid van Moolenbroek
282*9f20bfa6SDavid van Moolenbroek if (o)
283*9f20bfa6SDavid van Moolenbroek return D6_COPTION_DATA(o);
284*9f20bfa6SDavid van Moolenbroek return NULL;
285*9f20bfa6SDavid van Moolenbroek }
286*9f20bfa6SDavid van Moolenbroek
287*9f20bfa6SDavid van Moolenbroek static const struct dhcp6_option *
dhcp6_getmoption(uint16_t code,const struct dhcp6_message * m,size_t len)288*9f20bfa6SDavid van Moolenbroek dhcp6_getmoption(uint16_t code, const struct dhcp6_message *m, size_t len)
289*9f20bfa6SDavid van Moolenbroek {
290*9f20bfa6SDavid van Moolenbroek
291*9f20bfa6SDavid van Moolenbroek if (len < sizeof(*m)) {
292*9f20bfa6SDavid van Moolenbroek errno = EINVAL;
293*9f20bfa6SDavid van Moolenbroek return NULL;
294*9f20bfa6SDavid van Moolenbroek }
295*9f20bfa6SDavid van Moolenbroek len -= sizeof(*m);
296*9f20bfa6SDavid van Moolenbroek return dhcp6_findoption(code,
297*9f20bfa6SDavid van Moolenbroek (const uint8_t *)D6_CFIRST_OPTION(m), len);
298*9f20bfa6SDavid van Moolenbroek }
299*9f20bfa6SDavid van Moolenbroek
300*9f20bfa6SDavid van Moolenbroek static int
dhcp6_updateelapsed(struct interface * ifp,struct dhcp6_message * m,size_t len)301*9f20bfa6SDavid van Moolenbroek dhcp6_updateelapsed(struct interface *ifp, struct dhcp6_message *m, size_t len)
302*9f20bfa6SDavid van Moolenbroek {
303*9f20bfa6SDavid van Moolenbroek struct dhcp6_state *state;
304*9f20bfa6SDavid van Moolenbroek const struct dhcp6_option *co;
305*9f20bfa6SDavid van Moolenbroek struct dhcp6_option *o;
306*9f20bfa6SDavid van Moolenbroek struct timespec tv;
307*9f20bfa6SDavid van Moolenbroek time_t hsec;
308*9f20bfa6SDavid van Moolenbroek uint16_t u16;
309*9f20bfa6SDavid van Moolenbroek
310*9f20bfa6SDavid van Moolenbroek co = dhcp6_getmoption(D6_OPTION_ELAPSED, m, len);
311*9f20bfa6SDavid van Moolenbroek if (co == NULL)
312*9f20bfa6SDavid van Moolenbroek return -1;
313*9f20bfa6SDavid van Moolenbroek
314*9f20bfa6SDavid van Moolenbroek o = __UNCONST(co);
315*9f20bfa6SDavid van Moolenbroek state = D6_STATE(ifp);
316*9f20bfa6SDavid van Moolenbroek clock_gettime(CLOCK_MONOTONIC, &tv);
317*9f20bfa6SDavid van Moolenbroek if (state->RTC == 0) {
318*9f20bfa6SDavid van Moolenbroek /* An RTC of zero means we're the first message
319*9f20bfa6SDavid van Moolenbroek * out of the door, so the elapsed time is zero. */
320*9f20bfa6SDavid van Moolenbroek state->started = tv;
321*9f20bfa6SDavid van Moolenbroek hsec = 0;
322*9f20bfa6SDavid van Moolenbroek } else {
323*9f20bfa6SDavid van Moolenbroek timespecsub(&tv, &state->started, &tv);
324*9f20bfa6SDavid van Moolenbroek /* Elapsed time is measured in centiseconds.
325*9f20bfa6SDavid van Moolenbroek * We need to be sure it will not potentially overflow. */
326*9f20bfa6SDavid van Moolenbroek if (tv.tv_sec >= (UINT16_MAX / CSEC_PER_SEC) + 1)
327*9f20bfa6SDavid van Moolenbroek hsec = UINT16_MAX;
328*9f20bfa6SDavid van Moolenbroek else {
329*9f20bfa6SDavid van Moolenbroek hsec = (tv.tv_sec * CSEC_PER_SEC) +
330*9f20bfa6SDavid van Moolenbroek (tv.tv_nsec / NSEC_PER_CSEC);
331*9f20bfa6SDavid van Moolenbroek if (hsec > UINT16_MAX)
332*9f20bfa6SDavid van Moolenbroek hsec = UINT16_MAX;
333*9f20bfa6SDavid van Moolenbroek }
334*9f20bfa6SDavid van Moolenbroek }
335*9f20bfa6SDavid van Moolenbroek u16 = htons((uint16_t)hsec);
336*9f20bfa6SDavid van Moolenbroek memcpy(D6_OPTION_DATA(o), &u16, sizeof(u16));
337*9f20bfa6SDavid van Moolenbroek return 0;
338*9f20bfa6SDavid van Moolenbroek }
339*9f20bfa6SDavid van Moolenbroek
340*9f20bfa6SDavid van Moolenbroek static void
dhcp6_newxid(const struct interface * ifp,struct dhcp6_message * m)341*9f20bfa6SDavid van Moolenbroek dhcp6_newxid(const struct interface *ifp, struct dhcp6_message *m)
342*9f20bfa6SDavid van Moolenbroek {
343*9f20bfa6SDavid van Moolenbroek uint32_t xid;
344*9f20bfa6SDavid van Moolenbroek
345*9f20bfa6SDavid van Moolenbroek if (ifp->options->options & DHCPCD_XID_HWADDR &&
346*9f20bfa6SDavid van Moolenbroek ifp->hwlen >= sizeof(xid))
347*9f20bfa6SDavid van Moolenbroek /* The lower bits are probably more unique on the network */
348*9f20bfa6SDavid van Moolenbroek memcpy(&xid, (ifp->hwaddr + ifp->hwlen) - sizeof(xid),
349*9f20bfa6SDavid van Moolenbroek sizeof(xid));
350*9f20bfa6SDavid van Moolenbroek else
351*9f20bfa6SDavid van Moolenbroek xid = arc4random();
352*9f20bfa6SDavid van Moolenbroek
353*9f20bfa6SDavid van Moolenbroek m->xid[0] = (xid >> 16) & 0xff;
354*9f20bfa6SDavid van Moolenbroek m->xid[1] = (xid >> 8) & 0xff;
355*9f20bfa6SDavid van Moolenbroek m->xid[2] = xid & 0xff;
356*9f20bfa6SDavid van Moolenbroek }
357*9f20bfa6SDavid van Moolenbroek
358*9f20bfa6SDavid van Moolenbroek static const struct if_sla *
dhcp6_findselfsla(struct interface * ifp,const uint8_t * iaid)359*9f20bfa6SDavid van Moolenbroek dhcp6_findselfsla(struct interface *ifp, const uint8_t *iaid)
360*9f20bfa6SDavid van Moolenbroek {
361*9f20bfa6SDavid van Moolenbroek size_t i, j;
362*9f20bfa6SDavid van Moolenbroek
363*9f20bfa6SDavid van Moolenbroek for (i = 0; i < ifp->options->ia_len; i++) {
364*9f20bfa6SDavid van Moolenbroek if (iaid == NULL ||
365*9f20bfa6SDavid van Moolenbroek memcmp(&ifp->options->ia[i].iaid, iaid,
366*9f20bfa6SDavid van Moolenbroek sizeof(ifp->options->ia[i].iaid)) == 0)
367*9f20bfa6SDavid van Moolenbroek {
368*9f20bfa6SDavid van Moolenbroek for (j = 0; j < ifp->options->ia[i].sla_len; j++) {
369*9f20bfa6SDavid van Moolenbroek if (strcmp(ifp->options->ia[i].sla[j].ifname,
370*9f20bfa6SDavid van Moolenbroek ifp->name) == 0)
371*9f20bfa6SDavid van Moolenbroek return &ifp->options->ia[i].sla[j];
372*9f20bfa6SDavid van Moolenbroek }
373*9f20bfa6SDavid van Moolenbroek }
374*9f20bfa6SDavid van Moolenbroek }
375*9f20bfa6SDavid van Moolenbroek return NULL;
376*9f20bfa6SDavid van Moolenbroek }
377*9f20bfa6SDavid van Moolenbroek
378*9f20bfa6SDavid van Moolenbroek
379*9f20bfa6SDavid van Moolenbroek #ifndef ffs32
380*9f20bfa6SDavid van Moolenbroek static int
ffs32(uint32_t n)381*9f20bfa6SDavid van Moolenbroek ffs32(uint32_t n)
382*9f20bfa6SDavid van Moolenbroek {
383*9f20bfa6SDavid van Moolenbroek int v;
384*9f20bfa6SDavid van Moolenbroek
385*9f20bfa6SDavid van Moolenbroek if (!n)
386*9f20bfa6SDavid van Moolenbroek return 0;
387*9f20bfa6SDavid van Moolenbroek
388*9f20bfa6SDavid van Moolenbroek v = 1;
389*9f20bfa6SDavid van Moolenbroek if ((n & 0x0000FFFFU) == 0) {
390*9f20bfa6SDavid van Moolenbroek n >>= 16;
391*9f20bfa6SDavid van Moolenbroek v += 16;
392*9f20bfa6SDavid van Moolenbroek }
393*9f20bfa6SDavid van Moolenbroek if ((n & 0x000000FFU) == 0) {
394*9f20bfa6SDavid van Moolenbroek n >>= 8;
395*9f20bfa6SDavid van Moolenbroek v += 8;
396*9f20bfa6SDavid van Moolenbroek }
397*9f20bfa6SDavid van Moolenbroek if ((n & 0x0000000FU) == 0) {
398*9f20bfa6SDavid van Moolenbroek n >>= 4;
399*9f20bfa6SDavid van Moolenbroek v += 4;
400*9f20bfa6SDavid van Moolenbroek }
401*9f20bfa6SDavid van Moolenbroek if ((n & 0x00000003U) == 0) {
402*9f20bfa6SDavid van Moolenbroek n >>= 2;
403*9f20bfa6SDavid van Moolenbroek v += 2;
404*9f20bfa6SDavid van Moolenbroek }
405*9f20bfa6SDavid van Moolenbroek if ((n & 0x00000001U) == 0)
406*9f20bfa6SDavid van Moolenbroek v += 1;
407*9f20bfa6SDavid van Moolenbroek
408*9f20bfa6SDavid van Moolenbroek return v;
409*9f20bfa6SDavid van Moolenbroek }
410*9f20bfa6SDavid van Moolenbroek #endif
411*9f20bfa6SDavid van Moolenbroek
412*9f20bfa6SDavid van Moolenbroek static int
dhcp6_delegateaddr(struct in6_addr * addr,struct interface * ifp,const struct ipv6_addr * prefix,const struct if_sla * sla,struct if_ia * ia)413*9f20bfa6SDavid van Moolenbroek dhcp6_delegateaddr(struct in6_addr *addr, struct interface *ifp,
414*9f20bfa6SDavid van Moolenbroek const struct ipv6_addr *prefix, const struct if_sla *sla, struct if_ia *ia)
415*9f20bfa6SDavid van Moolenbroek {
416*9f20bfa6SDavid van Moolenbroek struct dhcp6_state *state;
417*9f20bfa6SDavid van Moolenbroek struct if_sla asla;
418*9f20bfa6SDavid van Moolenbroek char sabuf[INET6_ADDRSTRLEN];
419*9f20bfa6SDavid van Moolenbroek const char *sa;
420*9f20bfa6SDavid van Moolenbroek
421*9f20bfa6SDavid van Moolenbroek state = D6_STATE(ifp);
422*9f20bfa6SDavid van Moolenbroek if (state == NULL) {
423*9f20bfa6SDavid van Moolenbroek ifp->if_data[IF_DATA_DHCP6] = calloc(1, sizeof(*state));
424*9f20bfa6SDavid van Moolenbroek state = D6_STATE(ifp);
425*9f20bfa6SDavid van Moolenbroek if (state == NULL) {
426*9f20bfa6SDavid van Moolenbroek logger(ifp->ctx, LOG_ERR, "%s: %m", __func__);
427*9f20bfa6SDavid van Moolenbroek return -1;
428*9f20bfa6SDavid van Moolenbroek }
429*9f20bfa6SDavid van Moolenbroek
430*9f20bfa6SDavid van Moolenbroek TAILQ_INIT(&state->addrs);
431*9f20bfa6SDavid van Moolenbroek state->state = DH6S_DELEGATED;
432*9f20bfa6SDavid van Moolenbroek state->reason = "DELEGATED6";
433*9f20bfa6SDavid van Moolenbroek }
434*9f20bfa6SDavid van Moolenbroek
435*9f20bfa6SDavid van Moolenbroek if (sla == NULL || sla->sla_set == 0) {
436*9f20bfa6SDavid van Moolenbroek asla.sla = ifp->index;
437*9f20bfa6SDavid van Moolenbroek asla.prefix_len = 0;
438*9f20bfa6SDavid van Moolenbroek sla = &asla;
439*9f20bfa6SDavid van Moolenbroek } else if (sla->prefix_len == 0) {
440*9f20bfa6SDavid van Moolenbroek asla.sla = sla->sla;
441*9f20bfa6SDavid van Moolenbroek if (asla.sla == 0)
442*9f20bfa6SDavid van Moolenbroek asla.prefix_len = prefix->prefix_len;
443*9f20bfa6SDavid van Moolenbroek else
444*9f20bfa6SDavid van Moolenbroek asla.prefix_len = 0;
445*9f20bfa6SDavid van Moolenbroek sla = &asla;
446*9f20bfa6SDavid van Moolenbroek }
447*9f20bfa6SDavid van Moolenbroek if (sla->prefix_len == 0) {
448*9f20bfa6SDavid van Moolenbroek uint32_t sla_max;
449*9f20bfa6SDavid van Moolenbroek int bits;
450*9f20bfa6SDavid van Moolenbroek
451*9f20bfa6SDavid van Moolenbroek if (ia->sla_max == 0) {
452*9f20bfa6SDavid van Moolenbroek const struct interface *ifi;
453*9f20bfa6SDavid van Moolenbroek
454*9f20bfa6SDavid van Moolenbroek sla_max = 0;
455*9f20bfa6SDavid van Moolenbroek TAILQ_FOREACH(ifi, ifp->ctx->ifaces, next) {
456*9f20bfa6SDavid van Moolenbroek if (ifi != ifp && ifi->index > sla_max)
457*9f20bfa6SDavid van Moolenbroek sla_max = ifi->index;
458*9f20bfa6SDavid van Moolenbroek }
459*9f20bfa6SDavid van Moolenbroek } else
460*9f20bfa6SDavid van Moolenbroek sla_max = ia->sla_max;
461*9f20bfa6SDavid van Moolenbroek
462*9f20bfa6SDavid van Moolenbroek bits = ffs32(sla_max);
463*9f20bfa6SDavid van Moolenbroek
464*9f20bfa6SDavid van Moolenbroek if (prefix->prefix_len + bits > UINT8_MAX)
465*9f20bfa6SDavid van Moolenbroek asla.prefix_len = UINT8_MAX;
466*9f20bfa6SDavid van Moolenbroek else {
467*9f20bfa6SDavid van Moolenbroek asla.prefix_len = (uint8_t)(prefix->prefix_len + bits);
468*9f20bfa6SDavid van Moolenbroek
469*9f20bfa6SDavid van Moolenbroek /* Make a 64 prefix by default, as this maks SLAAC
470*9f20bfa6SDavid van Moolenbroek * possible. Otherwise round up to the nearest octet. */
471*9f20bfa6SDavid van Moolenbroek if (asla.prefix_len <= 64)
472*9f20bfa6SDavid van Moolenbroek asla.prefix_len = 64;
473*9f20bfa6SDavid van Moolenbroek else
474*9f20bfa6SDavid van Moolenbroek asla.prefix_len = (uint8_t)ROUNDUP8(asla.prefix_len);
475*9f20bfa6SDavid van Moolenbroek
476*9f20bfa6SDavid van Moolenbroek }
477*9f20bfa6SDavid van Moolenbroek
478*9f20bfa6SDavid van Moolenbroek #define BIT(n) (1l << (n))
479*9f20bfa6SDavid van Moolenbroek #define BIT_MASK(len) (BIT(len) - 1)
480*9f20bfa6SDavid van Moolenbroek if (ia->sla_max == 0)
481*9f20bfa6SDavid van Moolenbroek /* Work out the real sla_max from our bits used */
482*9f20bfa6SDavid van Moolenbroek ia->sla_max = (uint32_t)BIT_MASK(asla.prefix_len -
483*9f20bfa6SDavid van Moolenbroek prefix->prefix_len);
484*9f20bfa6SDavid van Moolenbroek }
485*9f20bfa6SDavid van Moolenbroek
486*9f20bfa6SDavid van Moolenbroek if (ipv6_userprefix(&prefix->prefix, prefix->prefix_len,
487*9f20bfa6SDavid van Moolenbroek sla->sla, addr, sla->prefix_len) == -1)
488*9f20bfa6SDavid van Moolenbroek {
489*9f20bfa6SDavid van Moolenbroek sa = inet_ntop(AF_INET6, &prefix->prefix,
490*9f20bfa6SDavid van Moolenbroek sabuf, sizeof(sabuf));
491*9f20bfa6SDavid van Moolenbroek logger(ifp->ctx, LOG_ERR,
492*9f20bfa6SDavid van Moolenbroek "%s: invalid prefix %s/%d + %d/%d: %m",
493*9f20bfa6SDavid van Moolenbroek ifp->name, sa, prefix->prefix_len,
494*9f20bfa6SDavid van Moolenbroek sla->sla, sla->prefix_len);
495*9f20bfa6SDavid van Moolenbroek return -1;
496*9f20bfa6SDavid van Moolenbroek }
497*9f20bfa6SDavid van Moolenbroek
498*9f20bfa6SDavid van Moolenbroek if (prefix->prefix_exclude_len &&
499*9f20bfa6SDavid van Moolenbroek IN6_ARE_ADDR_EQUAL(addr, &prefix->prefix_exclude))
500*9f20bfa6SDavid van Moolenbroek {
501*9f20bfa6SDavid van Moolenbroek sa = inet_ntop(AF_INET6, &prefix->prefix_exclude,
502*9f20bfa6SDavid van Moolenbroek sabuf, sizeof(sabuf));
503*9f20bfa6SDavid van Moolenbroek logger(ifp->ctx, LOG_ERR,
504*9f20bfa6SDavid van Moolenbroek "%s: cannot delegate excluded prefix %s/%d",
505*9f20bfa6SDavid van Moolenbroek ifp->name, sa, prefix->prefix_exclude_len);
506*9f20bfa6SDavid van Moolenbroek return -1;
507*9f20bfa6SDavid van Moolenbroek }
508*9f20bfa6SDavid van Moolenbroek
509*9f20bfa6SDavid van Moolenbroek return sla->prefix_len;
510*9f20bfa6SDavid van Moolenbroek }
511*9f20bfa6SDavid van Moolenbroek
512*9f20bfa6SDavid van Moolenbroek int
dhcp6_has_public_addr(const struct interface * ifp)513*9f20bfa6SDavid van Moolenbroek dhcp6_has_public_addr(const struct interface *ifp)
514*9f20bfa6SDavid van Moolenbroek {
515*9f20bfa6SDavid van Moolenbroek const struct dhcp6_state *state = D6_CSTATE(ifp);
516*9f20bfa6SDavid van Moolenbroek const struct ipv6_addr *ia;
517*9f20bfa6SDavid van Moolenbroek
518*9f20bfa6SDavid van Moolenbroek if (state == NULL)
519*9f20bfa6SDavid van Moolenbroek return 0;
520*9f20bfa6SDavid van Moolenbroek TAILQ_FOREACH(ia, &state->addrs, next) {
521*9f20bfa6SDavid van Moolenbroek if (ipv6_publicaddr(ia))
522*9f20bfa6SDavid van Moolenbroek return 1;
523*9f20bfa6SDavid van Moolenbroek }
524*9f20bfa6SDavid van Moolenbroek return 0;
525*9f20bfa6SDavid van Moolenbroek }
526*9f20bfa6SDavid van Moolenbroek
527*9f20bfa6SDavid van Moolenbroek static int
dhcp6_makemessage(struct interface * ifp)528*9f20bfa6SDavid van Moolenbroek dhcp6_makemessage(struct interface *ifp)
529*9f20bfa6SDavid van Moolenbroek {
530*9f20bfa6SDavid van Moolenbroek struct dhcp6_state *state;
531*9f20bfa6SDavid van Moolenbroek struct dhcp6_message *m;
532*9f20bfa6SDavid van Moolenbroek struct dhcp6_option *o, *so, *eo;
533*9f20bfa6SDavid van Moolenbroek const struct dhcp6_option *si, *unicast;
534*9f20bfa6SDavid van Moolenbroek size_t l, n, len, ml;
535*9f20bfa6SDavid van Moolenbroek uint8_t u8, type;
536*9f20bfa6SDavid van Moolenbroek uint16_t u16, n_options, auth_len;
537*9f20bfa6SDavid van Moolenbroek struct if_options *ifo;
538*9f20bfa6SDavid van Moolenbroek const struct dhcp_opt *opt, *opt2;
539*9f20bfa6SDavid van Moolenbroek uint8_t IA, *p;
540*9f20bfa6SDavid van Moolenbroek const uint8_t *pp;
541*9f20bfa6SDavid van Moolenbroek uint32_t u32;
542*9f20bfa6SDavid van Moolenbroek const struct ipv6_addr *ap;
543*9f20bfa6SDavid van Moolenbroek char hbuf[HOSTNAME_MAX_LEN + 1];
544*9f20bfa6SDavid van Moolenbroek const char *hostname;
545*9f20bfa6SDavid van Moolenbroek int fqdn;
546*9f20bfa6SDavid van Moolenbroek struct dhcp6_ia_addr *iap;
547*9f20bfa6SDavid van Moolenbroek struct dhcp6_pd_addr *pdp;
548*9f20bfa6SDavid van Moolenbroek
549*9f20bfa6SDavid van Moolenbroek state = D6_STATE(ifp);
550*9f20bfa6SDavid van Moolenbroek if (state->send) {
551*9f20bfa6SDavid van Moolenbroek free(state->send);
552*9f20bfa6SDavid van Moolenbroek state->send = NULL;
553*9f20bfa6SDavid van Moolenbroek }
554*9f20bfa6SDavid van Moolenbroek
555*9f20bfa6SDavid van Moolenbroek ifo = ifp->options;
556*9f20bfa6SDavid van Moolenbroek fqdn = ifo->fqdn;
557*9f20bfa6SDavid van Moolenbroek
558*9f20bfa6SDavid van Moolenbroek if (fqdn == FQDN_DISABLE && ifo->options & DHCPCD_HOSTNAME) {
559*9f20bfa6SDavid van Moolenbroek /* We're sending the DHCPv4 hostname option, so send FQDN as
560*9f20bfa6SDavid van Moolenbroek * DHCPv6 has no FQDN option and DHCPv4 must not send
561*9f20bfa6SDavid van Moolenbroek * hostname and FQDN according to RFC4702 */
562*9f20bfa6SDavid van Moolenbroek fqdn = FQDN_BOTH;
563*9f20bfa6SDavid van Moolenbroek }
564*9f20bfa6SDavid van Moolenbroek if (fqdn != FQDN_DISABLE) {
565*9f20bfa6SDavid van Moolenbroek if (ifo->hostname[0] == '\0')
566*9f20bfa6SDavid van Moolenbroek hostname = get_hostname(hbuf, sizeof(hbuf),
567*9f20bfa6SDavid van Moolenbroek ifo->options & DHCPCD_HOSTNAME_SHORT ? 1 : 0);
568*9f20bfa6SDavid van Moolenbroek else
569*9f20bfa6SDavid van Moolenbroek hostname = ifo->hostname;
570*9f20bfa6SDavid van Moolenbroek } else
571*9f20bfa6SDavid van Moolenbroek hostname = NULL; /* appearse gcc */
572*9f20bfa6SDavid van Moolenbroek
573*9f20bfa6SDavid van Moolenbroek /* Work out option size first */
574*9f20bfa6SDavid van Moolenbroek n_options = 0;
575*9f20bfa6SDavid van Moolenbroek len = 0;
576*9f20bfa6SDavid van Moolenbroek si = NULL;
577*9f20bfa6SDavid van Moolenbroek if (state->state != DH6S_RELEASE) {
578*9f20bfa6SDavid van Moolenbroek for (l = 0, opt = ifp->ctx->dhcp6_opts;
579*9f20bfa6SDavid van Moolenbroek l < ifp->ctx->dhcp6_opts_len;
580*9f20bfa6SDavid van Moolenbroek l++, opt++)
581*9f20bfa6SDavid van Moolenbroek {
582*9f20bfa6SDavid van Moolenbroek for (n = 0, opt2 = ifo->dhcp6_override;
583*9f20bfa6SDavid van Moolenbroek n < ifo->dhcp6_override_len;
584*9f20bfa6SDavid van Moolenbroek n++, opt2++)
585*9f20bfa6SDavid van Moolenbroek {
586*9f20bfa6SDavid van Moolenbroek if (opt->option == opt2->option)
587*9f20bfa6SDavid van Moolenbroek break;
588*9f20bfa6SDavid van Moolenbroek }
589*9f20bfa6SDavid van Moolenbroek if (n < ifo->dhcp6_override_len)
590*9f20bfa6SDavid van Moolenbroek continue;
591*9f20bfa6SDavid van Moolenbroek if (!(opt->type & NOREQ) &&
592*9f20bfa6SDavid van Moolenbroek (opt->type & REQUEST ||
593*9f20bfa6SDavid van Moolenbroek has_option_mask(ifo->requestmask6, opt->option)))
594*9f20bfa6SDavid van Moolenbroek {
595*9f20bfa6SDavid van Moolenbroek n_options++;
596*9f20bfa6SDavid van Moolenbroek len += sizeof(u16);
597*9f20bfa6SDavid van Moolenbroek }
598*9f20bfa6SDavid van Moolenbroek }
599*9f20bfa6SDavid van Moolenbroek for (l = 0, opt = ifo->dhcp6_override;
600*9f20bfa6SDavid van Moolenbroek l < ifo->dhcp6_override_len;
601*9f20bfa6SDavid van Moolenbroek l++, opt++)
602*9f20bfa6SDavid van Moolenbroek {
603*9f20bfa6SDavid van Moolenbroek if (!(opt->type & NOREQ) &&
604*9f20bfa6SDavid van Moolenbroek (opt->type & REQUEST ||
605*9f20bfa6SDavid van Moolenbroek has_option_mask(ifo->requestmask6, opt->option)))
606*9f20bfa6SDavid van Moolenbroek {
607*9f20bfa6SDavid van Moolenbroek n_options++;
608*9f20bfa6SDavid van Moolenbroek len += sizeof(u16);
609*9f20bfa6SDavid van Moolenbroek }
610*9f20bfa6SDavid van Moolenbroek }
611*9f20bfa6SDavid van Moolenbroek if (dhcp6_findselfsla(ifp, NULL)) {
612*9f20bfa6SDavid van Moolenbroek n_options++;
613*9f20bfa6SDavid van Moolenbroek len += sizeof(u16);
614*9f20bfa6SDavid van Moolenbroek }
615*9f20bfa6SDavid van Moolenbroek if (len)
616*9f20bfa6SDavid van Moolenbroek len += sizeof(*o);
617*9f20bfa6SDavid van Moolenbroek
618*9f20bfa6SDavid van Moolenbroek if (fqdn != FQDN_DISABLE)
619*9f20bfa6SDavid van Moolenbroek len += sizeof(*o) + 1 + encode_rfc1035(hostname, NULL);
620*9f20bfa6SDavid van Moolenbroek
621*9f20bfa6SDavid van Moolenbroek if ((ifo->auth.options & DHCPCD_AUTH_SENDREQUIRE) !=
622*9f20bfa6SDavid van Moolenbroek DHCPCD_AUTH_SENDREQUIRE)
623*9f20bfa6SDavid van Moolenbroek len += sizeof(*o); /* Reconfigure Accept */
624*9f20bfa6SDavid van Moolenbroek }
625*9f20bfa6SDavid van Moolenbroek
626*9f20bfa6SDavid van Moolenbroek len += sizeof(*state->send);
627*9f20bfa6SDavid van Moolenbroek len += sizeof(*o) + ifp->ctx->duid_len;
628*9f20bfa6SDavid van Moolenbroek len += sizeof(*o) + sizeof(uint16_t); /* elapsed */
629*9f20bfa6SDavid van Moolenbroek len += sizeof(*o) + dhcp6_makevendor(NULL, ifp);
630*9f20bfa6SDavid van Moolenbroek
631*9f20bfa6SDavid van Moolenbroek /* IA */
632*9f20bfa6SDavid van Moolenbroek m = NULL;
633*9f20bfa6SDavid van Moolenbroek ml = 0;
634*9f20bfa6SDavid van Moolenbroek switch(state->state) {
635*9f20bfa6SDavid van Moolenbroek case DH6S_REQUEST:
636*9f20bfa6SDavid van Moolenbroek m = state->recv;
637*9f20bfa6SDavid van Moolenbroek ml = state->recv_len;
638*9f20bfa6SDavid van Moolenbroek /* FALLTHROUGH */
639*9f20bfa6SDavid van Moolenbroek case DH6S_RELEASE:
640*9f20bfa6SDavid van Moolenbroek /* FALLTHROUGH */
641*9f20bfa6SDavid van Moolenbroek case DH6S_RENEW:
642*9f20bfa6SDavid van Moolenbroek if (m == NULL) {
643*9f20bfa6SDavid van Moolenbroek m = state->new;
644*9f20bfa6SDavid van Moolenbroek ml = state->new_len;
645*9f20bfa6SDavid van Moolenbroek }
646*9f20bfa6SDavid van Moolenbroek si = dhcp6_getmoption(D6_OPTION_SERVERID, m, ml);
647*9f20bfa6SDavid van Moolenbroek if (si == NULL) {
648*9f20bfa6SDavid van Moolenbroek errno = ESRCH;
649*9f20bfa6SDavid van Moolenbroek return -1;
650*9f20bfa6SDavid van Moolenbroek }
651*9f20bfa6SDavid van Moolenbroek len += sizeof(*si) + ntohs(si->len);
652*9f20bfa6SDavid van Moolenbroek /* FALLTHROUGH */
653*9f20bfa6SDavid van Moolenbroek case DH6S_REBIND:
654*9f20bfa6SDavid van Moolenbroek /* FALLTHROUGH */
655*9f20bfa6SDavid van Moolenbroek case DH6S_CONFIRM:
656*9f20bfa6SDavid van Moolenbroek /* FALLTHROUGH */
657*9f20bfa6SDavid van Moolenbroek case DH6S_DISCOVER:
658*9f20bfa6SDavid van Moolenbroek if (m == NULL) {
659*9f20bfa6SDavid van Moolenbroek m = state->new;
660*9f20bfa6SDavid van Moolenbroek ml = state->new_len;
661*9f20bfa6SDavid van Moolenbroek }
662*9f20bfa6SDavid van Moolenbroek TAILQ_FOREACH(ap, &state->addrs, next) {
663*9f20bfa6SDavid van Moolenbroek if (ap->prefix_vltime == 0 &&
664*9f20bfa6SDavid van Moolenbroek !(ap->flags & IPV6_AF_REQUEST))
665*9f20bfa6SDavid van Moolenbroek continue;
666*9f20bfa6SDavid van Moolenbroek if (ap->ia_type == D6_OPTION_IA_PD) {
667*9f20bfa6SDavid van Moolenbroek len += sizeof(*o) + sizeof(u8) +
668*9f20bfa6SDavid van Moolenbroek sizeof(u32) + sizeof(u32) +
669*9f20bfa6SDavid van Moolenbroek sizeof(ap->prefix);
670*9f20bfa6SDavid van Moolenbroek if (ap->prefix_exclude_len)
671*9f20bfa6SDavid van Moolenbroek len += sizeof(*o) + 1 +
672*9f20bfa6SDavid van Moolenbroek (uint8_t)((ap->prefix_exclude_len -
673*9f20bfa6SDavid van Moolenbroek ap->prefix_len - 1) / NBBY) + 1;
674*9f20bfa6SDavid van Moolenbroek } else
675*9f20bfa6SDavid van Moolenbroek len += sizeof(*o) + sizeof(ap->addr) +
676*9f20bfa6SDavid van Moolenbroek sizeof(u32) + sizeof(u32);
677*9f20bfa6SDavid van Moolenbroek }
678*9f20bfa6SDavid van Moolenbroek /* FALLTHROUGH */
679*9f20bfa6SDavid van Moolenbroek case DH6S_INIT:
680*9f20bfa6SDavid van Moolenbroek len += ifo->ia_len * (sizeof(*o) + (sizeof(u32) * 3));
681*9f20bfa6SDavid van Moolenbroek IA = 1;
682*9f20bfa6SDavid van Moolenbroek break;
683*9f20bfa6SDavid van Moolenbroek default:
684*9f20bfa6SDavid van Moolenbroek IA = 0;
685*9f20bfa6SDavid van Moolenbroek }
686*9f20bfa6SDavid van Moolenbroek
687*9f20bfa6SDavid van Moolenbroek if (state->state == DH6S_DISCOVER &&
688*9f20bfa6SDavid van Moolenbroek !(ifp->ctx->options & DHCPCD_TEST) &&
689*9f20bfa6SDavid van Moolenbroek has_option_mask(ifo->requestmask6, D6_OPTION_RAPID_COMMIT))
690*9f20bfa6SDavid van Moolenbroek len += sizeof(*o);
691*9f20bfa6SDavid van Moolenbroek
692*9f20bfa6SDavid van Moolenbroek if (m == NULL) {
693*9f20bfa6SDavid van Moolenbroek m = state->new;
694*9f20bfa6SDavid van Moolenbroek ml = state->new_len;
695*9f20bfa6SDavid van Moolenbroek }
696*9f20bfa6SDavid van Moolenbroek unicast = NULL;
697*9f20bfa6SDavid van Moolenbroek /* Depending on state, get the unicast address */
698*9f20bfa6SDavid van Moolenbroek switch(state->state) {
699*9f20bfa6SDavid van Moolenbroek break;
700*9f20bfa6SDavid van Moolenbroek case DH6S_INIT: /* FALLTHROUGH */
701*9f20bfa6SDavid van Moolenbroek case DH6S_DISCOVER:
702*9f20bfa6SDavid van Moolenbroek type = DHCP6_SOLICIT;
703*9f20bfa6SDavid van Moolenbroek break;
704*9f20bfa6SDavid van Moolenbroek case DH6S_REQUEST:
705*9f20bfa6SDavid van Moolenbroek type = DHCP6_REQUEST;
706*9f20bfa6SDavid van Moolenbroek unicast = dhcp6_getmoption(D6_OPTION_UNICAST, m, ml);
707*9f20bfa6SDavid van Moolenbroek break;
708*9f20bfa6SDavid van Moolenbroek case DH6S_CONFIRM:
709*9f20bfa6SDavid van Moolenbroek type = DHCP6_CONFIRM;
710*9f20bfa6SDavid van Moolenbroek break;
711*9f20bfa6SDavid van Moolenbroek case DH6S_REBIND:
712*9f20bfa6SDavid van Moolenbroek type = DHCP6_REBIND;
713*9f20bfa6SDavid van Moolenbroek break;
714*9f20bfa6SDavid van Moolenbroek case DH6S_RENEW:
715*9f20bfa6SDavid van Moolenbroek type = DHCP6_RENEW;
716*9f20bfa6SDavid van Moolenbroek unicast = dhcp6_getmoption(D6_OPTION_UNICAST, m, ml);
717*9f20bfa6SDavid van Moolenbroek break;
718*9f20bfa6SDavid van Moolenbroek case DH6S_INFORM:
719*9f20bfa6SDavid van Moolenbroek type = DHCP6_INFORMATION_REQ;
720*9f20bfa6SDavid van Moolenbroek break;
721*9f20bfa6SDavid van Moolenbroek case DH6S_RELEASE:
722*9f20bfa6SDavid van Moolenbroek type = DHCP6_RELEASE;
723*9f20bfa6SDavid van Moolenbroek unicast = dhcp6_getmoption(D6_OPTION_UNICAST, m, ml);
724*9f20bfa6SDavid van Moolenbroek break;
725*9f20bfa6SDavid van Moolenbroek default:
726*9f20bfa6SDavid van Moolenbroek errno = EINVAL;
727*9f20bfa6SDavid van Moolenbroek return -1;
728*9f20bfa6SDavid van Moolenbroek }
729*9f20bfa6SDavid van Moolenbroek
730*9f20bfa6SDavid van Moolenbroek auth_len = 0;
731*9f20bfa6SDavid van Moolenbroek if (ifo->auth.options & DHCPCD_AUTH_SEND) {
732*9f20bfa6SDavid van Moolenbroek ssize_t alen = dhcp_auth_encode(&ifo->auth,
733*9f20bfa6SDavid van Moolenbroek state->auth.token, NULL, 0, 6, type, NULL, 0);
734*9f20bfa6SDavid van Moolenbroek if (alen != -1 && alen > UINT16_MAX) {
735*9f20bfa6SDavid van Moolenbroek errno = ERANGE;
736*9f20bfa6SDavid van Moolenbroek alen = -1;
737*9f20bfa6SDavid van Moolenbroek }
738*9f20bfa6SDavid van Moolenbroek if (alen == -1)
739*9f20bfa6SDavid van Moolenbroek logger(ifp->ctx, LOG_ERR,
740*9f20bfa6SDavid van Moolenbroek "%s: dhcp_auth_encode: %m", ifp->name);
741*9f20bfa6SDavid van Moolenbroek else if (alen != 0) {
742*9f20bfa6SDavid van Moolenbroek auth_len = (uint16_t)alen;
743*9f20bfa6SDavid van Moolenbroek len += sizeof(*o) + auth_len;
744*9f20bfa6SDavid van Moolenbroek }
745*9f20bfa6SDavid van Moolenbroek }
746*9f20bfa6SDavid van Moolenbroek
747*9f20bfa6SDavid van Moolenbroek state->send = malloc(len);
748*9f20bfa6SDavid van Moolenbroek if (state->send == NULL)
749*9f20bfa6SDavid van Moolenbroek return -1;
750*9f20bfa6SDavid van Moolenbroek
751*9f20bfa6SDavid van Moolenbroek state->send_len = len;
752*9f20bfa6SDavid van Moolenbroek state->send->type = type;
753*9f20bfa6SDavid van Moolenbroek
754*9f20bfa6SDavid van Moolenbroek /* If we found a unicast option, copy it to our state for sending */
755*9f20bfa6SDavid van Moolenbroek if (unicast && ntohs(unicast->len) == sizeof(state->unicast))
756*9f20bfa6SDavid van Moolenbroek memcpy(&state->unicast, D6_COPTION_DATA(unicast),
757*9f20bfa6SDavid van Moolenbroek sizeof(state->unicast));
758*9f20bfa6SDavid van Moolenbroek else
759*9f20bfa6SDavid van Moolenbroek state->unicast = in6addr_any;
760*9f20bfa6SDavid van Moolenbroek
761*9f20bfa6SDavid van Moolenbroek dhcp6_newxid(ifp, state->send);
762*9f20bfa6SDavid van Moolenbroek
763*9f20bfa6SDavid van Moolenbroek o = D6_FIRST_OPTION(state->send);
764*9f20bfa6SDavid van Moolenbroek o->code = htons(D6_OPTION_CLIENTID);
765*9f20bfa6SDavid van Moolenbroek o->len = htons((uint16_t)ifp->ctx->duid_len);
766*9f20bfa6SDavid van Moolenbroek memcpy(D6_OPTION_DATA(o), ifp->ctx->duid, ifp->ctx->duid_len);
767*9f20bfa6SDavid van Moolenbroek
768*9f20bfa6SDavid van Moolenbroek if (si) {
769*9f20bfa6SDavid van Moolenbroek o = D6_NEXT_OPTION(o);
770*9f20bfa6SDavid van Moolenbroek memcpy(o, si, sizeof(*si) + ntohs(si->len));
771*9f20bfa6SDavid van Moolenbroek }
772*9f20bfa6SDavid van Moolenbroek
773*9f20bfa6SDavid van Moolenbroek o = D6_NEXT_OPTION(o);
774*9f20bfa6SDavid van Moolenbroek o->code = htons(D6_OPTION_ELAPSED);
775*9f20bfa6SDavid van Moolenbroek o->len = htons(sizeof(uint16_t));
776*9f20bfa6SDavid van Moolenbroek p = D6_OPTION_DATA(o);
777*9f20bfa6SDavid van Moolenbroek memset(p, 0, sizeof(uint16_t));
778*9f20bfa6SDavid van Moolenbroek
779*9f20bfa6SDavid van Moolenbroek o = D6_NEXT_OPTION(o);
780*9f20bfa6SDavid van Moolenbroek dhcp6_makevendor(o, ifp);
781*9f20bfa6SDavid van Moolenbroek
782*9f20bfa6SDavid van Moolenbroek if (state->state == DH6S_DISCOVER &&
783*9f20bfa6SDavid van Moolenbroek !(ifp->ctx->options & DHCPCD_TEST) &&
784*9f20bfa6SDavid van Moolenbroek has_option_mask(ifo->requestmask6, D6_OPTION_RAPID_COMMIT))
785*9f20bfa6SDavid van Moolenbroek {
786*9f20bfa6SDavid van Moolenbroek o = D6_NEXT_OPTION(o);
787*9f20bfa6SDavid van Moolenbroek o->code = htons(D6_OPTION_RAPID_COMMIT);
788*9f20bfa6SDavid van Moolenbroek o->len = 0;
789*9f20bfa6SDavid van Moolenbroek }
790*9f20bfa6SDavid van Moolenbroek
791*9f20bfa6SDavid van Moolenbroek for (l = 0; IA && l < ifo->ia_len; l++) {
792*9f20bfa6SDavid van Moolenbroek o = D6_NEXT_OPTION(o);
793*9f20bfa6SDavid van Moolenbroek o->code = htons(ifo->ia[l].ia_type);
794*9f20bfa6SDavid van Moolenbroek o->len = htons(sizeof(u32) + sizeof(u32) + sizeof(u32));
795*9f20bfa6SDavid van Moolenbroek p = D6_OPTION_DATA(o);
796*9f20bfa6SDavid van Moolenbroek memcpy(p, ifo->ia[l].iaid, sizeof(u32));
797*9f20bfa6SDavid van Moolenbroek p += sizeof(u32);
798*9f20bfa6SDavid van Moolenbroek memset(p, 0, sizeof(u32) + sizeof(u32));
799*9f20bfa6SDavid van Moolenbroek TAILQ_FOREACH(ap, &state->addrs, next) {
800*9f20bfa6SDavid van Moolenbroek if (ap->prefix_vltime == 0 &&
801*9f20bfa6SDavid van Moolenbroek !(ap->flags & IPV6_AF_REQUEST))
802*9f20bfa6SDavid van Moolenbroek continue;
803*9f20bfa6SDavid van Moolenbroek if (memcmp(ifo->ia[l].iaid, ap->iaid, sizeof(u32)))
804*9f20bfa6SDavid van Moolenbroek continue;
805*9f20bfa6SDavid van Moolenbroek so = D6_NEXT_OPTION(o);
806*9f20bfa6SDavid van Moolenbroek if (ap->ia_type == D6_OPTION_IA_PD) {
807*9f20bfa6SDavid van Moolenbroek so->code = htons(D6_OPTION_IAPREFIX);
808*9f20bfa6SDavid van Moolenbroek so->len = htons(sizeof(ap->prefix) +
809*9f20bfa6SDavid van Moolenbroek sizeof(u32) + sizeof(u32) + sizeof(u8));
810*9f20bfa6SDavid van Moolenbroek pdp = (struct dhcp6_pd_addr *)
811*9f20bfa6SDavid van Moolenbroek D6_OPTION_DATA(so);
812*9f20bfa6SDavid van Moolenbroek pdp->pltime = htonl(ap->prefix_pltime);
813*9f20bfa6SDavid van Moolenbroek pdp->vltime = htonl(ap->prefix_vltime);
814*9f20bfa6SDavid van Moolenbroek pdp->prefix_len = ap->prefix_len;
815*9f20bfa6SDavid van Moolenbroek pdp->prefix = ap->prefix;
816*9f20bfa6SDavid van Moolenbroek
817*9f20bfa6SDavid van Moolenbroek /* RFC6603 Section 4.2 */
818*9f20bfa6SDavid van Moolenbroek if (ap->prefix_exclude_len) {
819*9f20bfa6SDavid van Moolenbroek n = (size_t)((ap->prefix_exclude_len -
820*9f20bfa6SDavid van Moolenbroek ap->prefix_len - 1) / NBBY) + 1;
821*9f20bfa6SDavid van Moolenbroek eo = D6_NEXT_OPTION(so);
822*9f20bfa6SDavid van Moolenbroek eo->code = htons(D6_OPTION_PD_EXCLUDE);
823*9f20bfa6SDavid van Moolenbroek eo->len = (uint16_t)(n + 1);
824*9f20bfa6SDavid van Moolenbroek p = D6_OPTION_DATA(eo);
825*9f20bfa6SDavid van Moolenbroek *p++ = (uint8_t)ap->prefix_exclude_len;
826*9f20bfa6SDavid van Moolenbroek pp = ap->prefix_exclude.s6_addr;
827*9f20bfa6SDavid van Moolenbroek pp += (size_t)((ap->prefix_len - 1) / NBBY)
828*9f20bfa6SDavid van Moolenbroek + (n - 1);
829*9f20bfa6SDavid van Moolenbroek u8 = ap->prefix_len % NBBY;
830*9f20bfa6SDavid van Moolenbroek if (u8)
831*9f20bfa6SDavid van Moolenbroek n--;
832*9f20bfa6SDavid van Moolenbroek while (n-- > 0)
833*9f20bfa6SDavid van Moolenbroek *p++ = *pp--;
834*9f20bfa6SDavid van Moolenbroek if (u8)
835*9f20bfa6SDavid van Moolenbroek *p = (uint8_t)(*pp << u8);
836*9f20bfa6SDavid van Moolenbroek u16 = (uint16_t)(ntohs(so->len) +
837*9f20bfa6SDavid van Moolenbroek sizeof(*eo) + eo->len);
838*9f20bfa6SDavid van Moolenbroek so->len = htons(u16);
839*9f20bfa6SDavid van Moolenbroek eo->len = htons(eo->len);
840*9f20bfa6SDavid van Moolenbroek }
841*9f20bfa6SDavid van Moolenbroek
842*9f20bfa6SDavid van Moolenbroek u16 = (uint16_t)(ntohs(o->len) + sizeof(*so)
843*9f20bfa6SDavid van Moolenbroek + ntohs(so->len));
844*9f20bfa6SDavid van Moolenbroek o->len = htons(u16);
845*9f20bfa6SDavid van Moolenbroek } else {
846*9f20bfa6SDavid van Moolenbroek so->code = htons(D6_OPTION_IA_ADDR);
847*9f20bfa6SDavid van Moolenbroek so->len = sizeof(ap->addr) +
848*9f20bfa6SDavid van Moolenbroek sizeof(u32) + sizeof(u32);
849*9f20bfa6SDavid van Moolenbroek iap = (struct dhcp6_ia_addr *)
850*9f20bfa6SDavid van Moolenbroek D6_OPTION_DATA(so);
851*9f20bfa6SDavid van Moolenbroek iap->addr = ap->addr;
852*9f20bfa6SDavid van Moolenbroek iap->pltime = htonl(ap->prefix_pltime);
853*9f20bfa6SDavid van Moolenbroek iap->vltime = htonl(ap->prefix_vltime);
854*9f20bfa6SDavid van Moolenbroek u16 = (uint16_t)(ntohs(o->len) + sizeof(*so)
855*9f20bfa6SDavid van Moolenbroek + so->len);
856*9f20bfa6SDavid van Moolenbroek so->len = htons(so->len);
857*9f20bfa6SDavid van Moolenbroek o->len = htons(u16);
858*9f20bfa6SDavid van Moolenbroek }
859*9f20bfa6SDavid van Moolenbroek }
860*9f20bfa6SDavid van Moolenbroek }
861*9f20bfa6SDavid van Moolenbroek
862*9f20bfa6SDavid van Moolenbroek if (state->send->type != DHCP6_RELEASE) {
863*9f20bfa6SDavid van Moolenbroek if (fqdn != FQDN_DISABLE) {
864*9f20bfa6SDavid van Moolenbroek o = D6_NEXT_OPTION(o);
865*9f20bfa6SDavid van Moolenbroek o->code = htons(D6_OPTION_FQDN);
866*9f20bfa6SDavid van Moolenbroek p = D6_OPTION_DATA(o);
867*9f20bfa6SDavid van Moolenbroek switch (fqdn) {
868*9f20bfa6SDavid van Moolenbroek case FQDN_BOTH:
869*9f20bfa6SDavid van Moolenbroek *p = D6_FQDN_BOTH;
870*9f20bfa6SDavid van Moolenbroek break;
871*9f20bfa6SDavid van Moolenbroek case FQDN_PTR:
872*9f20bfa6SDavid van Moolenbroek *p = D6_FQDN_PTR;
873*9f20bfa6SDavid van Moolenbroek break;
874*9f20bfa6SDavid van Moolenbroek default:
875*9f20bfa6SDavid van Moolenbroek *p = D6_FQDN_NONE;
876*9f20bfa6SDavid van Moolenbroek break;
877*9f20bfa6SDavid van Moolenbroek }
878*9f20bfa6SDavid van Moolenbroek l = encode_rfc1035(hostname, p + 1);
879*9f20bfa6SDavid van Moolenbroek if (l == 0)
880*9f20bfa6SDavid van Moolenbroek *p = D6_FQDN_NONE;
881*9f20bfa6SDavid van Moolenbroek o->len = htons((uint16_t)(l + 1));
882*9f20bfa6SDavid van Moolenbroek }
883*9f20bfa6SDavid van Moolenbroek
884*9f20bfa6SDavid van Moolenbroek if ((ifo->auth.options & DHCPCD_AUTH_SENDREQUIRE) !=
885*9f20bfa6SDavid van Moolenbroek DHCPCD_AUTH_SENDREQUIRE)
886*9f20bfa6SDavid van Moolenbroek {
887*9f20bfa6SDavid van Moolenbroek o = D6_NEXT_OPTION(o);
888*9f20bfa6SDavid van Moolenbroek o->code = htons(D6_OPTION_RECONF_ACCEPT);
889*9f20bfa6SDavid van Moolenbroek o->len = 0;
890*9f20bfa6SDavid van Moolenbroek }
891*9f20bfa6SDavid van Moolenbroek
892*9f20bfa6SDavid van Moolenbroek if (n_options) {
893*9f20bfa6SDavid van Moolenbroek o = D6_NEXT_OPTION(o);
894*9f20bfa6SDavid van Moolenbroek o->code = htons(D6_OPTION_ORO);
895*9f20bfa6SDavid van Moolenbroek o->len = 0;
896*9f20bfa6SDavid van Moolenbroek p = D6_OPTION_DATA(o);
897*9f20bfa6SDavid van Moolenbroek for (l = 0, opt = ifp->ctx->dhcp6_opts;
898*9f20bfa6SDavid van Moolenbroek l < ifp->ctx->dhcp6_opts_len;
899*9f20bfa6SDavid van Moolenbroek l++, opt++)
900*9f20bfa6SDavid van Moolenbroek {
901*9f20bfa6SDavid van Moolenbroek for (n = 0, opt2 = ifo->dhcp6_override;
902*9f20bfa6SDavid van Moolenbroek n < ifo->dhcp6_override_len;
903*9f20bfa6SDavid van Moolenbroek n++, opt2++)
904*9f20bfa6SDavid van Moolenbroek {
905*9f20bfa6SDavid van Moolenbroek if (opt->option == opt2->option)
906*9f20bfa6SDavid van Moolenbroek break;
907*9f20bfa6SDavid van Moolenbroek }
908*9f20bfa6SDavid van Moolenbroek if (n < ifo->dhcp6_override_len)
909*9f20bfa6SDavid van Moolenbroek continue;
910*9f20bfa6SDavid van Moolenbroek if (!(opt->type & NOREQ) &&
911*9f20bfa6SDavid van Moolenbroek (opt->type & REQUEST ||
912*9f20bfa6SDavid van Moolenbroek has_option_mask(ifo->requestmask6,
913*9f20bfa6SDavid van Moolenbroek opt->option)))
914*9f20bfa6SDavid van Moolenbroek {
915*9f20bfa6SDavid van Moolenbroek u16 = htons((uint16_t)opt->option);
916*9f20bfa6SDavid van Moolenbroek memcpy(p, &u16, sizeof(u16));
917*9f20bfa6SDavid van Moolenbroek p += sizeof(u16);
918*9f20bfa6SDavid van Moolenbroek o->len = (uint16_t)(o->len + sizeof(u16));
919*9f20bfa6SDavid van Moolenbroek }
920*9f20bfa6SDavid van Moolenbroek }
921*9f20bfa6SDavid van Moolenbroek for (l = 0, opt = ifo->dhcp6_override;
922*9f20bfa6SDavid van Moolenbroek l < ifo->dhcp6_override_len;
923*9f20bfa6SDavid van Moolenbroek l++, opt++)
924*9f20bfa6SDavid van Moolenbroek {
925*9f20bfa6SDavid van Moolenbroek if (!(opt->type & NOREQ) &&
926*9f20bfa6SDavid van Moolenbroek (opt->type & REQUEST ||
927*9f20bfa6SDavid van Moolenbroek has_option_mask(ifo->requestmask6,
928*9f20bfa6SDavid van Moolenbroek opt->option)))
929*9f20bfa6SDavid van Moolenbroek {
930*9f20bfa6SDavid van Moolenbroek u16 = htons((uint16_t)opt->option);
931*9f20bfa6SDavid van Moolenbroek memcpy(p, &u16, sizeof(u16));
932*9f20bfa6SDavid van Moolenbroek p += sizeof(u16);
933*9f20bfa6SDavid van Moolenbroek o->len = (uint16_t)(o->len + sizeof(u16));
934*9f20bfa6SDavid van Moolenbroek }
935*9f20bfa6SDavid van Moolenbroek }
936*9f20bfa6SDavid van Moolenbroek if (dhcp6_findselfsla(ifp, NULL)) {
937*9f20bfa6SDavid van Moolenbroek u16 = htons(D6_OPTION_PD_EXCLUDE);
938*9f20bfa6SDavid van Moolenbroek memcpy(p, &u16, sizeof(u16));
939*9f20bfa6SDavid van Moolenbroek o->len = (uint16_t)(o->len + sizeof(u16));
940*9f20bfa6SDavid van Moolenbroek }
941*9f20bfa6SDavid van Moolenbroek o->len = htons(o->len);
942*9f20bfa6SDavid van Moolenbroek }
943*9f20bfa6SDavid van Moolenbroek }
944*9f20bfa6SDavid van Moolenbroek
945*9f20bfa6SDavid van Moolenbroek /* This has to be the last option */
946*9f20bfa6SDavid van Moolenbroek if (ifo->auth.options & DHCPCD_AUTH_SEND && auth_len != 0) {
947*9f20bfa6SDavid van Moolenbroek o = D6_NEXT_OPTION(o);
948*9f20bfa6SDavid van Moolenbroek o->code = htons(D6_OPTION_AUTH);
949*9f20bfa6SDavid van Moolenbroek o->len = htons((uint16_t)auth_len);
950*9f20bfa6SDavid van Moolenbroek /* data will be filled at send message time */
951*9f20bfa6SDavid van Moolenbroek }
952*9f20bfa6SDavid van Moolenbroek
953*9f20bfa6SDavid van Moolenbroek return 0;
954*9f20bfa6SDavid van Moolenbroek }
955*9f20bfa6SDavid van Moolenbroek
956*9f20bfa6SDavid van Moolenbroek static const char *
dhcp6_get_op(uint16_t type)957*9f20bfa6SDavid van Moolenbroek dhcp6_get_op(uint16_t type)
958*9f20bfa6SDavid van Moolenbroek {
959*9f20bfa6SDavid van Moolenbroek const struct dhcp6_op *d;
960*9f20bfa6SDavid van Moolenbroek
961*9f20bfa6SDavid van Moolenbroek for (d = dhcp6_ops; d->name; d++)
962*9f20bfa6SDavid van Moolenbroek if (d->type == type)
963*9f20bfa6SDavid van Moolenbroek return d->name;
964*9f20bfa6SDavid van Moolenbroek return NULL;
965*9f20bfa6SDavid van Moolenbroek }
966*9f20bfa6SDavid van Moolenbroek
967*9f20bfa6SDavid van Moolenbroek static void
dhcp6_freedrop_addrs(struct interface * ifp,int drop,const struct interface * ifd)968*9f20bfa6SDavid van Moolenbroek dhcp6_freedrop_addrs(struct interface *ifp, int drop,
969*9f20bfa6SDavid van Moolenbroek const struct interface *ifd)
970*9f20bfa6SDavid van Moolenbroek {
971*9f20bfa6SDavid van Moolenbroek struct dhcp6_state *state;
972*9f20bfa6SDavid van Moolenbroek
973*9f20bfa6SDavid van Moolenbroek state = D6_STATE(ifp);
974*9f20bfa6SDavid van Moolenbroek if (state) {
975*9f20bfa6SDavid van Moolenbroek ipv6_freedrop_addrs(&state->addrs, drop, ifd);
976*9f20bfa6SDavid van Moolenbroek if (drop)
977*9f20bfa6SDavid van Moolenbroek ipv6_buildroutes(ifp->ctx);
978*9f20bfa6SDavid van Moolenbroek }
979*9f20bfa6SDavid van Moolenbroek }
980*9f20bfa6SDavid van Moolenbroek
dhcp6_delete_delegates(struct interface * ifp)981*9f20bfa6SDavid van Moolenbroek static void dhcp6_delete_delegates(struct interface *ifp)
982*9f20bfa6SDavid van Moolenbroek {
983*9f20bfa6SDavid van Moolenbroek struct interface *ifp0;
984*9f20bfa6SDavid van Moolenbroek
985*9f20bfa6SDavid van Moolenbroek if (ifp->ctx->ifaces) {
986*9f20bfa6SDavid van Moolenbroek TAILQ_FOREACH(ifp0, ifp->ctx->ifaces, next) {
987*9f20bfa6SDavid van Moolenbroek if (ifp0 != ifp)
988*9f20bfa6SDavid van Moolenbroek dhcp6_freedrop_addrs(ifp0, 1, ifp);
989*9f20bfa6SDavid van Moolenbroek }
990*9f20bfa6SDavid van Moolenbroek }
991*9f20bfa6SDavid van Moolenbroek }
992*9f20bfa6SDavid van Moolenbroek
993*9f20bfa6SDavid van Moolenbroek static ssize_t
dhcp6_update_auth(struct interface * ifp,struct dhcp6_message * m,size_t len)994*9f20bfa6SDavid van Moolenbroek dhcp6_update_auth(struct interface *ifp, struct dhcp6_message *m, size_t len)
995*9f20bfa6SDavid van Moolenbroek {
996*9f20bfa6SDavid van Moolenbroek struct dhcp6_state *state;
997*9f20bfa6SDavid van Moolenbroek const struct dhcp6_option *co;
998*9f20bfa6SDavid van Moolenbroek struct dhcp6_option *o;
999*9f20bfa6SDavid van Moolenbroek
1000*9f20bfa6SDavid van Moolenbroek co = dhcp6_getmoption(D6_OPTION_AUTH, m, len);
1001*9f20bfa6SDavid van Moolenbroek if (co == NULL)
1002*9f20bfa6SDavid van Moolenbroek return -1;
1003*9f20bfa6SDavid van Moolenbroek
1004*9f20bfa6SDavid van Moolenbroek o = __UNCONST(co);
1005*9f20bfa6SDavid van Moolenbroek state = D6_STATE(ifp);
1006*9f20bfa6SDavid van Moolenbroek
1007*9f20bfa6SDavid van Moolenbroek return dhcp_auth_encode(&ifp->options->auth, state->auth.token,
1008*9f20bfa6SDavid van Moolenbroek (uint8_t *)state->send, state->send_len,
1009*9f20bfa6SDavid van Moolenbroek 6, state->send->type,
1010*9f20bfa6SDavid van Moolenbroek D6_OPTION_DATA(o), ntohs(o->len));
1011*9f20bfa6SDavid van Moolenbroek }
1012*9f20bfa6SDavid van Moolenbroek
1013*9f20bfa6SDavid van Moolenbroek static int
dhcp6_sendmessage(struct interface * ifp,void (* callback)(void *))1014*9f20bfa6SDavid van Moolenbroek dhcp6_sendmessage(struct interface *ifp, void (*callback)(void *))
1015*9f20bfa6SDavid van Moolenbroek {
1016*9f20bfa6SDavid van Moolenbroek struct dhcp6_state *state;
1017*9f20bfa6SDavid van Moolenbroek struct ipv6_ctx *ctx;
1018*9f20bfa6SDavid van Moolenbroek struct sockaddr_in6 dst;
1019*9f20bfa6SDavid van Moolenbroek struct cmsghdr *cm;
1020*9f20bfa6SDavid van Moolenbroek struct in6_pktinfo pi;
1021*9f20bfa6SDavid van Moolenbroek struct timespec RTprev;
1022*9f20bfa6SDavid van Moolenbroek double rnd;
1023*9f20bfa6SDavid van Moolenbroek time_t ms;
1024*9f20bfa6SDavid van Moolenbroek uint8_t neg;
1025*9f20bfa6SDavid van Moolenbroek const char *broad_uni;
1026*9f20bfa6SDavid van Moolenbroek const struct in6_addr alldhcp = IN6ADDR_LINKLOCAL_ALLDHCP_INIT;
1027*9f20bfa6SDavid van Moolenbroek
1028*9f20bfa6SDavid van Moolenbroek if (!callback && ifp->carrier == LINK_DOWN)
1029*9f20bfa6SDavid van Moolenbroek return 0;
1030*9f20bfa6SDavid van Moolenbroek
1031*9f20bfa6SDavid van Moolenbroek memset(&dst, 0, sizeof(dst));
1032*9f20bfa6SDavid van Moolenbroek dst.sin6_family = AF_INET6;
1033*9f20bfa6SDavid van Moolenbroek dst.sin6_port = htons(DHCP6_SERVER_PORT);
1034*9f20bfa6SDavid van Moolenbroek #ifdef SIN6_LEN
1035*9f20bfa6SDavid van Moolenbroek dst.sin6_len = sizeof(dst);
1036*9f20bfa6SDavid van Moolenbroek #endif
1037*9f20bfa6SDavid van Moolenbroek
1038*9f20bfa6SDavid van Moolenbroek state = D6_STATE(ifp);
1039*9f20bfa6SDavid van Moolenbroek /* We need to ensure we have sufficient scope to unicast the address */
1040*9f20bfa6SDavid van Moolenbroek /* XXX FIXME: We should check any added addresses we have like from
1041*9f20bfa6SDavid van Moolenbroek * a Router Advertisement */
1042*9f20bfa6SDavid van Moolenbroek if (IN6_IS_ADDR_UNSPECIFIED(&state->unicast) ||
1043*9f20bfa6SDavid van Moolenbroek (state->state == DH6S_REQUEST &&
1044*9f20bfa6SDavid van Moolenbroek (!IN6_IS_ADDR_LINKLOCAL(&state->unicast) || !ipv6_linklocal(ifp))))
1045*9f20bfa6SDavid van Moolenbroek {
1046*9f20bfa6SDavid van Moolenbroek dst.sin6_addr = alldhcp;
1047*9f20bfa6SDavid van Moolenbroek broad_uni = "broadcasting";
1048*9f20bfa6SDavid van Moolenbroek } else {
1049*9f20bfa6SDavid van Moolenbroek dst.sin6_addr = state->unicast;
1050*9f20bfa6SDavid van Moolenbroek broad_uni = "unicasting";
1051*9f20bfa6SDavid van Moolenbroek }
1052*9f20bfa6SDavid van Moolenbroek
1053*9f20bfa6SDavid van Moolenbroek if (!callback)
1054*9f20bfa6SDavid van Moolenbroek logger(ifp->ctx, LOG_DEBUG,
1055*9f20bfa6SDavid van Moolenbroek "%s: %s %s with xid 0x%02x%02x%02x",
1056*9f20bfa6SDavid van Moolenbroek ifp->name,
1057*9f20bfa6SDavid van Moolenbroek broad_uni,
1058*9f20bfa6SDavid van Moolenbroek dhcp6_get_op(state->send->type),
1059*9f20bfa6SDavid van Moolenbroek state->send->xid[0],
1060*9f20bfa6SDavid van Moolenbroek state->send->xid[1],
1061*9f20bfa6SDavid van Moolenbroek state->send->xid[2]);
1062*9f20bfa6SDavid van Moolenbroek else {
1063*9f20bfa6SDavid van Moolenbroek if (state->IMD &&
1064*9f20bfa6SDavid van Moolenbroek !(ifp->options->options & DHCPCD_INITIAL_DELAY))
1065*9f20bfa6SDavid van Moolenbroek state->IMD = 0;
1066*9f20bfa6SDavid van Moolenbroek if (state->IMD) {
1067*9f20bfa6SDavid van Moolenbroek /* Some buggy PPP servers close the link too early
1068*9f20bfa6SDavid van Moolenbroek * after sending an invalid status in their reply
1069*9f20bfa6SDavid van Moolenbroek * which means this host won't see it.
1070*9f20bfa6SDavid van Moolenbroek * 1 second grace seems to be the sweet spot. */
1071*9f20bfa6SDavid van Moolenbroek if (ifp->flags & IFF_POINTOPOINT)
1072*9f20bfa6SDavid van Moolenbroek state->RT.tv_sec = 1;
1073*9f20bfa6SDavid van Moolenbroek else
1074*9f20bfa6SDavid van Moolenbroek state->RT.tv_sec = 0;
1075*9f20bfa6SDavid van Moolenbroek state->RT.tv_nsec = (suseconds_t)arc4random_uniform(
1076*9f20bfa6SDavid van Moolenbroek (uint32_t)(state->IMD * NSEC_PER_SEC));
1077*9f20bfa6SDavid van Moolenbroek timespecnorm(&state->RT);
1078*9f20bfa6SDavid van Moolenbroek broad_uni = "delaying";
1079*9f20bfa6SDavid van Moolenbroek goto logsend;
1080*9f20bfa6SDavid van Moolenbroek }
1081*9f20bfa6SDavid van Moolenbroek if (state->RTC == 0) {
1082*9f20bfa6SDavid van Moolenbroek RTprev.tv_sec = state->IRT;
1083*9f20bfa6SDavid van Moolenbroek RTprev.tv_nsec = 0;
1084*9f20bfa6SDavid van Moolenbroek state->RT.tv_sec = RTprev.tv_sec;
1085*9f20bfa6SDavid van Moolenbroek state->RT.tv_nsec = 0;
1086*9f20bfa6SDavid van Moolenbroek } else {
1087*9f20bfa6SDavid van Moolenbroek RTprev = state->RT;
1088*9f20bfa6SDavid van Moolenbroek timespecadd(&state->RT, &state->RT, &state->RT);
1089*9f20bfa6SDavid van Moolenbroek }
1090*9f20bfa6SDavid van Moolenbroek
1091*9f20bfa6SDavid van Moolenbroek rnd = DHCP6_RAND_MIN;
1092*9f20bfa6SDavid van Moolenbroek rnd += (suseconds_t)arc4random_uniform(
1093*9f20bfa6SDavid van Moolenbroek DHCP6_RAND_MAX - DHCP6_RAND_MIN);
1094*9f20bfa6SDavid van Moolenbroek rnd /= MSEC_PER_SEC;
1095*9f20bfa6SDavid van Moolenbroek neg = (rnd < 0.0);
1096*9f20bfa6SDavid van Moolenbroek if (neg)
1097*9f20bfa6SDavid van Moolenbroek rnd = -rnd;
1098*9f20bfa6SDavid van Moolenbroek ts_to_ms(ms, &RTprev);
1099*9f20bfa6SDavid van Moolenbroek ms = (time_t)((double)ms * rnd);
1100*9f20bfa6SDavid van Moolenbroek ms_to_ts(&RTprev, ms);
1101*9f20bfa6SDavid van Moolenbroek if (neg)
1102*9f20bfa6SDavid van Moolenbroek timespecsub(&state->RT, &RTprev, &state->RT);
1103*9f20bfa6SDavid van Moolenbroek else
1104*9f20bfa6SDavid van Moolenbroek timespecadd(&state->RT, &RTprev, &state->RT);
1105*9f20bfa6SDavid van Moolenbroek
1106*9f20bfa6SDavid van Moolenbroek if (state->MRT != 0 && state->RT.tv_sec > state->MRT) {
1107*9f20bfa6SDavid van Moolenbroek RTprev.tv_sec = state->MRT;
1108*9f20bfa6SDavid van Moolenbroek RTprev.tv_nsec = 0;
1109*9f20bfa6SDavid van Moolenbroek state->RT.tv_sec = state->MRT;
1110*9f20bfa6SDavid van Moolenbroek state->RT.tv_nsec = 0;
1111*9f20bfa6SDavid van Moolenbroek ts_to_ms(ms, &RTprev);
1112*9f20bfa6SDavid van Moolenbroek ms = (time_t)((double)ms * rnd);
1113*9f20bfa6SDavid van Moolenbroek ms_to_ts(&RTprev, ms);
1114*9f20bfa6SDavid van Moolenbroek if (neg)
1115*9f20bfa6SDavid van Moolenbroek timespecsub(&state->RT, &RTprev, &state->RT);
1116*9f20bfa6SDavid van Moolenbroek else
1117*9f20bfa6SDavid van Moolenbroek timespecadd(&state->RT, &RTprev, &state->RT);
1118*9f20bfa6SDavid van Moolenbroek }
1119*9f20bfa6SDavid van Moolenbroek
1120*9f20bfa6SDavid van Moolenbroek logsend:
1121*9f20bfa6SDavid van Moolenbroek if (ifp->carrier != LINK_DOWN)
1122*9f20bfa6SDavid van Moolenbroek logger(ifp->ctx, LOG_DEBUG,
1123*9f20bfa6SDavid van Moolenbroek "%s: %s %s (xid 0x%02x%02x%02x),"
1124*9f20bfa6SDavid van Moolenbroek " next in %0.1f seconds",
1125*9f20bfa6SDavid van Moolenbroek ifp->name,
1126*9f20bfa6SDavid van Moolenbroek broad_uni,
1127*9f20bfa6SDavid van Moolenbroek dhcp6_get_op(state->send->type),
1128*9f20bfa6SDavid van Moolenbroek state->send->xid[0],
1129*9f20bfa6SDavid van Moolenbroek state->send->xid[1],
1130*9f20bfa6SDavid van Moolenbroek state->send->xid[2],
1131*9f20bfa6SDavid van Moolenbroek timespec_to_double(&state->RT));
1132*9f20bfa6SDavid van Moolenbroek
1133*9f20bfa6SDavid van Moolenbroek /* This sometimes happens when we delegate to this interface
1134*9f20bfa6SDavid van Moolenbroek * AND run DHCPv6 on it normally. */
1135*9f20bfa6SDavid van Moolenbroek assert(timespec_to_double(&state->RT) != 0);
1136*9f20bfa6SDavid van Moolenbroek
1137*9f20bfa6SDavid van Moolenbroek /* Wait the initial delay */
1138*9f20bfa6SDavid van Moolenbroek if (state->IMD != 0) {
1139*9f20bfa6SDavid van Moolenbroek state->IMD = 0;
1140*9f20bfa6SDavid van Moolenbroek eloop_timeout_add_tv(ifp->ctx->eloop,
1141*9f20bfa6SDavid van Moolenbroek &state->RT, callback, ifp);
1142*9f20bfa6SDavid van Moolenbroek return 0;
1143*9f20bfa6SDavid van Moolenbroek }
1144*9f20bfa6SDavid van Moolenbroek }
1145*9f20bfa6SDavid van Moolenbroek
1146*9f20bfa6SDavid van Moolenbroek if (ifp->carrier == LINK_DOWN)
1147*9f20bfa6SDavid van Moolenbroek return 0;
1148*9f20bfa6SDavid van Moolenbroek
1149*9f20bfa6SDavid van Moolenbroek /* Update the elapsed time */
1150*9f20bfa6SDavid van Moolenbroek dhcp6_updateelapsed(ifp, state->send, state->send_len);
1151*9f20bfa6SDavid van Moolenbroek if (ifp->options->auth.options & DHCPCD_AUTH_SEND &&
1152*9f20bfa6SDavid van Moolenbroek dhcp6_update_auth(ifp, state->send, state->send_len) == -1)
1153*9f20bfa6SDavid van Moolenbroek {
1154*9f20bfa6SDavid van Moolenbroek logger(ifp->ctx, LOG_ERR,
1155*9f20bfa6SDavid van Moolenbroek "%s: dhcp6_updateauth: %m", ifp->name);
1156*9f20bfa6SDavid van Moolenbroek if (errno != ESRCH)
1157*9f20bfa6SDavid van Moolenbroek return -1;
1158*9f20bfa6SDavid van Moolenbroek }
1159*9f20bfa6SDavid van Moolenbroek
1160*9f20bfa6SDavid van Moolenbroek ctx = ifp->ctx->ipv6;
1161*9f20bfa6SDavid van Moolenbroek dst.sin6_scope_id = ifp->index;
1162*9f20bfa6SDavid van Moolenbroek ctx->sndhdr.msg_name = (void *)&dst;
1163*9f20bfa6SDavid van Moolenbroek ctx->sndhdr.msg_iov[0].iov_base = state->send;
1164*9f20bfa6SDavid van Moolenbroek ctx->sndhdr.msg_iov[0].iov_len = state->send_len;
1165*9f20bfa6SDavid van Moolenbroek
1166*9f20bfa6SDavid van Moolenbroek /* Set the outbound interface */
1167*9f20bfa6SDavid van Moolenbroek cm = CMSG_FIRSTHDR(&ctx->sndhdr);
1168*9f20bfa6SDavid van Moolenbroek if (cm == NULL) /* unlikely */
1169*9f20bfa6SDavid van Moolenbroek return -1;
1170*9f20bfa6SDavid van Moolenbroek cm->cmsg_level = IPPROTO_IPV6;
1171*9f20bfa6SDavid van Moolenbroek cm->cmsg_type = IPV6_PKTINFO;
1172*9f20bfa6SDavid van Moolenbroek cm->cmsg_len = CMSG_LEN(sizeof(pi));
1173*9f20bfa6SDavid van Moolenbroek memset(&pi, 0, sizeof(pi));
1174*9f20bfa6SDavid van Moolenbroek pi.ipi6_ifindex = ifp->index;
1175*9f20bfa6SDavid van Moolenbroek memcpy(CMSG_DATA(cm), &pi, sizeof(pi));
1176*9f20bfa6SDavid van Moolenbroek
1177*9f20bfa6SDavid van Moolenbroek if (sendmsg(ctx->dhcp_fd, &ctx->sndhdr, 0) == -1) {
1178*9f20bfa6SDavid van Moolenbroek logger(ifp->ctx, LOG_ERR,
1179*9f20bfa6SDavid van Moolenbroek "%s: %s: sendmsg: %m", ifp->name, __func__);
1180*9f20bfa6SDavid van Moolenbroek ifp->options->options &= ~DHCPCD_IPV6;
1181*9f20bfa6SDavid van Moolenbroek dhcp6_drop(ifp, "EXPIRE6");
1182*9f20bfa6SDavid van Moolenbroek return -1;
1183*9f20bfa6SDavid van Moolenbroek }
1184*9f20bfa6SDavid van Moolenbroek
1185*9f20bfa6SDavid van Moolenbroek state->RTC++;
1186*9f20bfa6SDavid van Moolenbroek if (callback) {
1187*9f20bfa6SDavid van Moolenbroek if (state->MRC == 0 || state->RTC < state->MRC)
1188*9f20bfa6SDavid van Moolenbroek eloop_timeout_add_tv(ifp->ctx->eloop,
1189*9f20bfa6SDavid van Moolenbroek &state->RT, callback, ifp);
1190*9f20bfa6SDavid van Moolenbroek else if (state->MRC != 0 && state->MRCcallback)
1191*9f20bfa6SDavid van Moolenbroek eloop_timeout_add_tv(ifp->ctx->eloop,
1192*9f20bfa6SDavid van Moolenbroek &state->RT, state->MRCcallback, ifp);
1193*9f20bfa6SDavid van Moolenbroek else
1194*9f20bfa6SDavid van Moolenbroek logger(ifp->ctx, LOG_WARNING,
1195*9f20bfa6SDavid van Moolenbroek "%s: sent %d times with no reply",
1196*9f20bfa6SDavid van Moolenbroek ifp->name, state->RTC);
1197*9f20bfa6SDavid van Moolenbroek }
1198*9f20bfa6SDavid van Moolenbroek return 0;
1199*9f20bfa6SDavid van Moolenbroek }
1200*9f20bfa6SDavid van Moolenbroek
1201*9f20bfa6SDavid van Moolenbroek static void
dhcp6_sendinform(void * arg)1202*9f20bfa6SDavid van Moolenbroek dhcp6_sendinform(void *arg)
1203*9f20bfa6SDavid van Moolenbroek {
1204*9f20bfa6SDavid van Moolenbroek
1205*9f20bfa6SDavid van Moolenbroek dhcp6_sendmessage(arg, dhcp6_sendinform);
1206*9f20bfa6SDavid van Moolenbroek }
1207*9f20bfa6SDavid van Moolenbroek
1208*9f20bfa6SDavid van Moolenbroek static void
dhcp6_senddiscover(void * arg)1209*9f20bfa6SDavid van Moolenbroek dhcp6_senddiscover(void *arg)
1210*9f20bfa6SDavid van Moolenbroek {
1211*9f20bfa6SDavid van Moolenbroek
1212*9f20bfa6SDavid van Moolenbroek dhcp6_sendmessage(arg, dhcp6_senddiscover);
1213*9f20bfa6SDavid van Moolenbroek }
1214*9f20bfa6SDavid van Moolenbroek
1215*9f20bfa6SDavid van Moolenbroek static void
dhcp6_sendrequest(void * arg)1216*9f20bfa6SDavid van Moolenbroek dhcp6_sendrequest(void *arg)
1217*9f20bfa6SDavid van Moolenbroek {
1218*9f20bfa6SDavid van Moolenbroek
1219*9f20bfa6SDavid van Moolenbroek dhcp6_sendmessage(arg, dhcp6_sendrequest);
1220*9f20bfa6SDavid van Moolenbroek }
1221*9f20bfa6SDavid van Moolenbroek
1222*9f20bfa6SDavid van Moolenbroek static void
dhcp6_sendrebind(void * arg)1223*9f20bfa6SDavid van Moolenbroek dhcp6_sendrebind(void *arg)
1224*9f20bfa6SDavid van Moolenbroek {
1225*9f20bfa6SDavid van Moolenbroek
1226*9f20bfa6SDavid van Moolenbroek dhcp6_sendmessage(arg, dhcp6_sendrebind);
1227*9f20bfa6SDavid van Moolenbroek }
1228*9f20bfa6SDavid van Moolenbroek
1229*9f20bfa6SDavid van Moolenbroek static void
dhcp6_sendrenew(void * arg)1230*9f20bfa6SDavid van Moolenbroek dhcp6_sendrenew(void *arg)
1231*9f20bfa6SDavid van Moolenbroek {
1232*9f20bfa6SDavid van Moolenbroek
1233*9f20bfa6SDavid van Moolenbroek dhcp6_sendmessage(arg, dhcp6_sendrenew);
1234*9f20bfa6SDavid van Moolenbroek }
1235*9f20bfa6SDavid van Moolenbroek
1236*9f20bfa6SDavid van Moolenbroek static void
dhcp6_sendconfirm(void * arg)1237*9f20bfa6SDavid van Moolenbroek dhcp6_sendconfirm(void *arg)
1238*9f20bfa6SDavid van Moolenbroek {
1239*9f20bfa6SDavid van Moolenbroek
1240*9f20bfa6SDavid van Moolenbroek dhcp6_sendmessage(arg, dhcp6_sendconfirm);
1241*9f20bfa6SDavid van Moolenbroek }
1242*9f20bfa6SDavid van Moolenbroek
1243*9f20bfa6SDavid van Moolenbroek static void
dhcp6_sendrelease(void * arg)1244*9f20bfa6SDavid van Moolenbroek dhcp6_sendrelease(void *arg)
1245*9f20bfa6SDavid van Moolenbroek {
1246*9f20bfa6SDavid van Moolenbroek
1247*9f20bfa6SDavid van Moolenbroek dhcp6_sendmessage(arg, dhcp6_sendrelease);
1248*9f20bfa6SDavid van Moolenbroek }
1249*9f20bfa6SDavid van Moolenbroek
1250*9f20bfa6SDavid van Moolenbroek static void
dhcp6_startrenew(void * arg)1251*9f20bfa6SDavid van Moolenbroek dhcp6_startrenew(void *arg)
1252*9f20bfa6SDavid van Moolenbroek {
1253*9f20bfa6SDavid van Moolenbroek struct interface *ifp;
1254*9f20bfa6SDavid van Moolenbroek struct dhcp6_state *state;
1255*9f20bfa6SDavid van Moolenbroek
1256*9f20bfa6SDavid van Moolenbroek ifp = arg;
1257*9f20bfa6SDavid van Moolenbroek state = D6_STATE(ifp);
1258*9f20bfa6SDavid van Moolenbroek state->state = DH6S_RENEW;
1259*9f20bfa6SDavid van Moolenbroek state->RTC = 0;
1260*9f20bfa6SDavid van Moolenbroek state->IRT = REN_TIMEOUT;
1261*9f20bfa6SDavid van Moolenbroek state->MRT = REN_MAX_RT;
1262*9f20bfa6SDavid van Moolenbroek state->MRC = 0;
1263*9f20bfa6SDavid van Moolenbroek
1264*9f20bfa6SDavid van Moolenbroek if (dhcp6_makemessage(ifp) == -1)
1265*9f20bfa6SDavid van Moolenbroek logger(ifp->ctx, LOG_ERR,
1266*9f20bfa6SDavid van Moolenbroek "%s: dhcp6_makemessage: %m", ifp->name);
1267*9f20bfa6SDavid van Moolenbroek else
1268*9f20bfa6SDavid van Moolenbroek dhcp6_sendrenew(ifp);
1269*9f20bfa6SDavid van Moolenbroek }
1270*9f20bfa6SDavid van Moolenbroek
1271*9f20bfa6SDavid van Moolenbroek int
dhcp6_dadcompleted(const struct interface * ifp)1272*9f20bfa6SDavid van Moolenbroek dhcp6_dadcompleted(const struct interface *ifp)
1273*9f20bfa6SDavid van Moolenbroek {
1274*9f20bfa6SDavid van Moolenbroek const struct dhcp6_state *state;
1275*9f20bfa6SDavid van Moolenbroek const struct ipv6_addr *ap;
1276*9f20bfa6SDavid van Moolenbroek
1277*9f20bfa6SDavid van Moolenbroek state = D6_CSTATE(ifp);
1278*9f20bfa6SDavid van Moolenbroek TAILQ_FOREACH(ap, &state->addrs, next) {
1279*9f20bfa6SDavid van Moolenbroek if (ap->flags & IPV6_AF_ADDED &&
1280*9f20bfa6SDavid van Moolenbroek !(ap->flags & IPV6_AF_DADCOMPLETED))
1281*9f20bfa6SDavid van Moolenbroek return 0;
1282*9f20bfa6SDavid van Moolenbroek }
1283*9f20bfa6SDavid van Moolenbroek return 1;
1284*9f20bfa6SDavid van Moolenbroek }
1285*9f20bfa6SDavid van Moolenbroek
1286*9f20bfa6SDavid van Moolenbroek static void
dhcp6_dadcallback(void * arg)1287*9f20bfa6SDavid van Moolenbroek dhcp6_dadcallback(void *arg)
1288*9f20bfa6SDavid van Moolenbroek {
1289*9f20bfa6SDavid van Moolenbroek struct ipv6_addr *ap = arg;
1290*9f20bfa6SDavid van Moolenbroek struct interface *ifp;
1291*9f20bfa6SDavid van Moolenbroek struct dhcp6_state *state;
1292*9f20bfa6SDavid van Moolenbroek int wascompleted, valid;
1293*9f20bfa6SDavid van Moolenbroek
1294*9f20bfa6SDavid van Moolenbroek wascompleted = (ap->flags & IPV6_AF_DADCOMPLETED);
1295*9f20bfa6SDavid van Moolenbroek ap->flags |= IPV6_AF_DADCOMPLETED;
1296*9f20bfa6SDavid van Moolenbroek if (ap->flags & IPV6_AF_DUPLICATED)
1297*9f20bfa6SDavid van Moolenbroek /* XXX FIXME
1298*9f20bfa6SDavid van Moolenbroek * We should decline the address */
1299*9f20bfa6SDavid van Moolenbroek logger(ap->iface->ctx, LOG_WARNING, "%s: DAD detected %s",
1300*9f20bfa6SDavid van Moolenbroek ap->iface->name, ap->saddr);
1301*9f20bfa6SDavid van Moolenbroek
1302*9f20bfa6SDavid van Moolenbroek if (!wascompleted) {
1303*9f20bfa6SDavid van Moolenbroek ifp = ap->iface;
1304*9f20bfa6SDavid van Moolenbroek state = D6_STATE(ifp);
1305*9f20bfa6SDavid van Moolenbroek if (state->state == DH6S_BOUND ||
1306*9f20bfa6SDavid van Moolenbroek state->state == DH6S_DELEGATED)
1307*9f20bfa6SDavid van Moolenbroek {
1308*9f20bfa6SDavid van Moolenbroek struct ipv6_addr *ap2;
1309*9f20bfa6SDavid van Moolenbroek
1310*9f20bfa6SDavid van Moolenbroek valid = (ap->delegating_iface == NULL);
1311*9f20bfa6SDavid van Moolenbroek TAILQ_FOREACH(ap2, &state->addrs, next) {
1312*9f20bfa6SDavid van Moolenbroek if (ap2->flags & IPV6_AF_ADDED &&
1313*9f20bfa6SDavid van Moolenbroek !(ap2->flags & IPV6_AF_DADCOMPLETED))
1314*9f20bfa6SDavid van Moolenbroek {
1315*9f20bfa6SDavid van Moolenbroek wascompleted = 1;
1316*9f20bfa6SDavid van Moolenbroek break;
1317*9f20bfa6SDavid van Moolenbroek }
1318*9f20bfa6SDavid van Moolenbroek }
1319*9f20bfa6SDavid van Moolenbroek if (!wascompleted) {
1320*9f20bfa6SDavid van Moolenbroek logger(ap->iface->ctx, LOG_DEBUG,
1321*9f20bfa6SDavid van Moolenbroek "%s: DHCPv6 DAD completed", ifp->name);
1322*9f20bfa6SDavid van Moolenbroek script_runreason(ifp,
1323*9f20bfa6SDavid van Moolenbroek ap->delegating_iface ?
1324*9f20bfa6SDavid van Moolenbroek "DELEGATED6" : state->reason);
1325*9f20bfa6SDavid van Moolenbroek if (valid)
1326*9f20bfa6SDavid van Moolenbroek dhcpcd_daemonise(ifp->ctx);
1327*9f20bfa6SDavid van Moolenbroek }
1328*9f20bfa6SDavid van Moolenbroek }
1329*9f20bfa6SDavid van Moolenbroek }
1330*9f20bfa6SDavid van Moolenbroek }
1331*9f20bfa6SDavid van Moolenbroek
1332*9f20bfa6SDavid van Moolenbroek static void
dhcp6_addrequestedaddrs(struct interface * ifp)1333*9f20bfa6SDavid van Moolenbroek dhcp6_addrequestedaddrs(struct interface *ifp)
1334*9f20bfa6SDavid van Moolenbroek {
1335*9f20bfa6SDavid van Moolenbroek struct dhcp6_state *state;
1336*9f20bfa6SDavid van Moolenbroek size_t i;
1337*9f20bfa6SDavid van Moolenbroek struct if_ia *ia;
1338*9f20bfa6SDavid van Moolenbroek struct ipv6_addr *a;
1339*9f20bfa6SDavid van Moolenbroek char iabuf[INET6_ADDRSTRLEN];
1340*9f20bfa6SDavid van Moolenbroek const char *iap;
1341*9f20bfa6SDavid van Moolenbroek
1342*9f20bfa6SDavid van Moolenbroek state = D6_STATE(ifp);
1343*9f20bfa6SDavid van Moolenbroek /* Add any requested prefixes / addresses */
1344*9f20bfa6SDavid van Moolenbroek for (i = 0; i < ifp->options->ia_len; i++) {
1345*9f20bfa6SDavid van Moolenbroek ia = &ifp->options->ia[i];
1346*9f20bfa6SDavid van Moolenbroek if (!((ia->ia_type == D6_OPTION_IA_PD && ia->prefix_len) ||
1347*9f20bfa6SDavid van Moolenbroek !IN6_IS_ADDR_UNSPECIFIED(&ia->addr)))
1348*9f20bfa6SDavid van Moolenbroek continue;
1349*9f20bfa6SDavid van Moolenbroek a = calloc(1, sizeof(*a));
1350*9f20bfa6SDavid van Moolenbroek if (a == NULL) {
1351*9f20bfa6SDavid van Moolenbroek logger(ifp->ctx, LOG_ERR, "%s: %m", __func__);
1352*9f20bfa6SDavid van Moolenbroek return;
1353*9f20bfa6SDavid van Moolenbroek }
1354*9f20bfa6SDavid van Moolenbroek a->flags = IPV6_AF_REQUEST;
1355*9f20bfa6SDavid van Moolenbroek a->iface = ifp;
1356*9f20bfa6SDavid van Moolenbroek a->dadcallback = dhcp6_dadcallback;
1357*9f20bfa6SDavid van Moolenbroek memcpy(&a->iaid, &ia->iaid, sizeof(a->iaid));
1358*9f20bfa6SDavid van Moolenbroek a->ia_type = ia->ia_type;
1359*9f20bfa6SDavid van Moolenbroek //a->prefix_pltime = 0;
1360*9f20bfa6SDavid van Moolenbroek //a->prefix_vltime = 0;
1361*9f20bfa6SDavid van Moolenbroek
1362*9f20bfa6SDavid van Moolenbroek if (ia->ia_type == D6_OPTION_IA_PD) {
1363*9f20bfa6SDavid van Moolenbroek memcpy(&a->prefix, &ia->addr, sizeof(a->addr));
1364*9f20bfa6SDavid van Moolenbroek a->prefix_len = ia->prefix_len;
1365*9f20bfa6SDavid van Moolenbroek iap = inet_ntop(AF_INET6, &a->prefix,
1366*9f20bfa6SDavid van Moolenbroek iabuf, sizeof(iabuf));
1367*9f20bfa6SDavid van Moolenbroek } else {
1368*9f20bfa6SDavid van Moolenbroek memcpy(&a->addr, &ia->addr, sizeof(a->addr));
1369*9f20bfa6SDavid van Moolenbroek /*
1370*9f20bfa6SDavid van Moolenbroek * RFC 5942 Section 5
1371*9f20bfa6SDavid van Moolenbroek * We cannot assume any prefix length, nor tie the
1372*9f20bfa6SDavid van Moolenbroek * address to an existing one as it could expire
1373*9f20bfa6SDavid van Moolenbroek * before the address.
1374*9f20bfa6SDavid van Moolenbroek * As such we just give it a 128 prefix.
1375*9f20bfa6SDavid van Moolenbroek */
1376*9f20bfa6SDavid van Moolenbroek a->prefix_len = 128;
1377*9f20bfa6SDavid van Moolenbroek ipv6_makeprefix(&a->prefix, &a->addr, a->prefix_len);
1378*9f20bfa6SDavid van Moolenbroek iap = inet_ntop(AF_INET6, &a->addr,
1379*9f20bfa6SDavid van Moolenbroek iabuf, sizeof(iabuf));
1380*9f20bfa6SDavid van Moolenbroek }
1381*9f20bfa6SDavid van Moolenbroek snprintf(a->saddr, sizeof(a->saddr),
1382*9f20bfa6SDavid van Moolenbroek "%s/%d", iap, a->prefix_len);
1383*9f20bfa6SDavid van Moolenbroek TAILQ_INSERT_TAIL(&state->addrs, a, next);
1384*9f20bfa6SDavid van Moolenbroek }
1385*9f20bfa6SDavid van Moolenbroek }
1386*9f20bfa6SDavid van Moolenbroek
1387*9f20bfa6SDavid van Moolenbroek static void
dhcp6_startdiscover(void * arg)1388*9f20bfa6SDavid van Moolenbroek dhcp6_startdiscover(void *arg)
1389*9f20bfa6SDavid van Moolenbroek {
1390*9f20bfa6SDavid van Moolenbroek struct interface *ifp;
1391*9f20bfa6SDavid van Moolenbroek struct dhcp6_state *state;
1392*9f20bfa6SDavid van Moolenbroek
1393*9f20bfa6SDavid van Moolenbroek ifp = arg;
1394*9f20bfa6SDavid van Moolenbroek dhcp6_delete_delegates(ifp);
1395*9f20bfa6SDavid van Moolenbroek logger(ifp->ctx, LOG_INFO, "%s: soliciting a DHCPv6 lease", ifp->name);
1396*9f20bfa6SDavid van Moolenbroek state = D6_STATE(ifp);
1397*9f20bfa6SDavid van Moolenbroek state->state = DH6S_DISCOVER;
1398*9f20bfa6SDavid van Moolenbroek state->RTC = 0;
1399*9f20bfa6SDavid van Moolenbroek state->IMD = SOL_MAX_DELAY;
1400*9f20bfa6SDavid van Moolenbroek state->IRT = SOL_TIMEOUT;
1401*9f20bfa6SDavid van Moolenbroek state->MRT = state->sol_max_rt;
1402*9f20bfa6SDavid van Moolenbroek state->MRC = 0;
1403*9f20bfa6SDavid van Moolenbroek
1404*9f20bfa6SDavid van Moolenbroek eloop_timeout_delete(ifp->ctx->eloop, NULL, ifp);
1405*9f20bfa6SDavid van Moolenbroek free(state->new);
1406*9f20bfa6SDavid van Moolenbroek state->new = NULL;
1407*9f20bfa6SDavid van Moolenbroek state->new_len = 0;
1408*9f20bfa6SDavid van Moolenbroek
1409*9f20bfa6SDavid van Moolenbroek dhcp6_freedrop_addrs(ifp, 0, NULL);
1410*9f20bfa6SDavid van Moolenbroek unlink(state->leasefile);
1411*9f20bfa6SDavid van Moolenbroek
1412*9f20bfa6SDavid van Moolenbroek dhcp6_addrequestedaddrs(ifp);
1413*9f20bfa6SDavid van Moolenbroek
1414*9f20bfa6SDavid van Moolenbroek if (dhcp6_makemessage(ifp) == -1)
1415*9f20bfa6SDavid van Moolenbroek logger(ifp->ctx, LOG_ERR,
1416*9f20bfa6SDavid van Moolenbroek "%s: dhcp6_makemessage: %m", ifp->name);
1417*9f20bfa6SDavid van Moolenbroek else
1418*9f20bfa6SDavid van Moolenbroek dhcp6_senddiscover(ifp);
1419*9f20bfa6SDavid van Moolenbroek }
1420*9f20bfa6SDavid van Moolenbroek
1421*9f20bfa6SDavid van Moolenbroek static void
dhcp6_failconfirm(void * arg)1422*9f20bfa6SDavid van Moolenbroek dhcp6_failconfirm(void *arg)
1423*9f20bfa6SDavid van Moolenbroek {
1424*9f20bfa6SDavid van Moolenbroek struct interface *ifp;
1425*9f20bfa6SDavid van Moolenbroek
1426*9f20bfa6SDavid van Moolenbroek ifp = arg;
1427*9f20bfa6SDavid van Moolenbroek logger(ifp->ctx, LOG_ERR,
1428*9f20bfa6SDavid van Moolenbroek "%s: failed to confirm prior address", ifp->name);
1429*9f20bfa6SDavid van Moolenbroek /* Section 18.1.2 says that we SHOULD use the last known
1430*9f20bfa6SDavid van Moolenbroek * IP address(s) and lifetimes if we didn't get a reply.
1431*9f20bfa6SDavid van Moolenbroek * I disagree with this. */
1432*9f20bfa6SDavid van Moolenbroek dhcp6_startdiscover(ifp);
1433*9f20bfa6SDavid van Moolenbroek }
1434*9f20bfa6SDavid van Moolenbroek
1435*9f20bfa6SDavid van Moolenbroek static void
dhcp6_failrequest(void * arg)1436*9f20bfa6SDavid van Moolenbroek dhcp6_failrequest(void *arg)
1437*9f20bfa6SDavid van Moolenbroek {
1438*9f20bfa6SDavid van Moolenbroek struct interface *ifp;
1439*9f20bfa6SDavid van Moolenbroek
1440*9f20bfa6SDavid van Moolenbroek ifp = arg;
1441*9f20bfa6SDavid van Moolenbroek logger(ifp->ctx, LOG_ERR, "%s: failed to request address", ifp->name);
1442*9f20bfa6SDavid van Moolenbroek /* Section 18.1.1 says that client local policy dictates
1443*9f20bfa6SDavid van Moolenbroek * what happens if a REQUEST fails.
1444*9f20bfa6SDavid van Moolenbroek * Of the possible scenarios listed, moving back to the
1445*9f20bfa6SDavid van Moolenbroek * DISCOVER phase makes more sense for us. */
1446*9f20bfa6SDavid van Moolenbroek dhcp6_startdiscover(ifp);
1447*9f20bfa6SDavid van Moolenbroek }
1448*9f20bfa6SDavid van Moolenbroek
1449*9f20bfa6SDavid van Moolenbroek static void
dhcp6_failrebind(void * arg)1450*9f20bfa6SDavid van Moolenbroek dhcp6_failrebind(void *arg)
1451*9f20bfa6SDavid van Moolenbroek {
1452*9f20bfa6SDavid van Moolenbroek struct interface *ifp;
1453*9f20bfa6SDavid van Moolenbroek
1454*9f20bfa6SDavid van Moolenbroek ifp = arg;
1455*9f20bfa6SDavid van Moolenbroek logger(ifp->ctx, LOG_ERR,
1456*9f20bfa6SDavid van Moolenbroek "%s: failed to rebind prior delegation", ifp->name);
1457*9f20bfa6SDavid van Moolenbroek dhcp6_delete_delegates(ifp);
1458*9f20bfa6SDavid van Moolenbroek /* Section 18.1.2 says that we SHOULD use the last known
1459*9f20bfa6SDavid van Moolenbroek * IP address(s) and lifetimes if we didn't get a reply.
1460*9f20bfa6SDavid van Moolenbroek * I disagree with this. */
1461*9f20bfa6SDavid van Moolenbroek dhcp6_startdiscover(ifp);
1462*9f20bfa6SDavid van Moolenbroek }
1463*9f20bfa6SDavid van Moolenbroek
1464*9f20bfa6SDavid van Moolenbroek
1465*9f20bfa6SDavid van Moolenbroek static int
dhcp6_hasprefixdelegation(struct interface * ifp)1466*9f20bfa6SDavid van Moolenbroek dhcp6_hasprefixdelegation(struct interface *ifp)
1467*9f20bfa6SDavid van Moolenbroek {
1468*9f20bfa6SDavid van Moolenbroek size_t i;
1469*9f20bfa6SDavid van Moolenbroek uint16_t t;
1470*9f20bfa6SDavid van Moolenbroek
1471*9f20bfa6SDavid van Moolenbroek t = 0;
1472*9f20bfa6SDavid van Moolenbroek for (i = 0; i < ifp->options->ia_len; i++) {
1473*9f20bfa6SDavid van Moolenbroek if (t && t != ifp->options->ia[i].ia_type) {
1474*9f20bfa6SDavid van Moolenbroek if (t == D6_OPTION_IA_PD ||
1475*9f20bfa6SDavid van Moolenbroek ifp->options->ia[i].ia_type == D6_OPTION_IA_PD)
1476*9f20bfa6SDavid van Moolenbroek return 2;
1477*9f20bfa6SDavid van Moolenbroek }
1478*9f20bfa6SDavid van Moolenbroek t = ifp->options->ia[i].ia_type;
1479*9f20bfa6SDavid van Moolenbroek }
1480*9f20bfa6SDavid van Moolenbroek return t == D6_OPTION_IA_PD ? 1 : 0;
1481*9f20bfa6SDavid van Moolenbroek }
1482*9f20bfa6SDavid van Moolenbroek
1483*9f20bfa6SDavid van Moolenbroek static void
dhcp6_startrebind(void * arg)1484*9f20bfa6SDavid van Moolenbroek dhcp6_startrebind(void *arg)
1485*9f20bfa6SDavid van Moolenbroek {
1486*9f20bfa6SDavid van Moolenbroek struct interface *ifp;
1487*9f20bfa6SDavid van Moolenbroek struct dhcp6_state *state;
1488*9f20bfa6SDavid van Moolenbroek int pd;
1489*9f20bfa6SDavid van Moolenbroek
1490*9f20bfa6SDavid van Moolenbroek ifp = arg;
1491*9f20bfa6SDavid van Moolenbroek eloop_timeout_delete(ifp->ctx->eloop, dhcp6_sendrenew, ifp);
1492*9f20bfa6SDavid van Moolenbroek state = D6_STATE(ifp);
1493*9f20bfa6SDavid van Moolenbroek if (state->state == DH6S_RENEW)
1494*9f20bfa6SDavid van Moolenbroek logger(ifp->ctx, LOG_WARNING,
1495*9f20bfa6SDavid van Moolenbroek "%s: failed to renew DHCPv6, rebinding", ifp->name);
1496*9f20bfa6SDavid van Moolenbroek else
1497*9f20bfa6SDavid van Moolenbroek logger(ifp->ctx, LOG_INFO,
1498*9f20bfa6SDavid van Moolenbroek "%s: rebinding prior DHCPv6 lease", ifp->name);
1499*9f20bfa6SDavid van Moolenbroek state->state = DH6S_REBIND;
1500*9f20bfa6SDavid van Moolenbroek state->RTC = 0;
1501*9f20bfa6SDavid van Moolenbroek state->MRC = 0;
1502*9f20bfa6SDavid van Moolenbroek
1503*9f20bfa6SDavid van Moolenbroek /* RFC 3633 section 12.1 */
1504*9f20bfa6SDavid van Moolenbroek pd = dhcp6_hasprefixdelegation(ifp);
1505*9f20bfa6SDavid van Moolenbroek if (pd) {
1506*9f20bfa6SDavid van Moolenbroek state->IMD = CNF_MAX_DELAY;
1507*9f20bfa6SDavid van Moolenbroek state->IRT = CNF_TIMEOUT;
1508*9f20bfa6SDavid van Moolenbroek state->MRT = CNF_MAX_RT;
1509*9f20bfa6SDavid van Moolenbroek } else {
1510*9f20bfa6SDavid van Moolenbroek state->IRT = REB_TIMEOUT;
1511*9f20bfa6SDavid van Moolenbroek state->MRT = REB_MAX_RT;
1512*9f20bfa6SDavid van Moolenbroek }
1513*9f20bfa6SDavid van Moolenbroek
1514*9f20bfa6SDavid van Moolenbroek if (dhcp6_makemessage(ifp) == -1)
1515*9f20bfa6SDavid van Moolenbroek logger(ifp->ctx, LOG_ERR,
1516*9f20bfa6SDavid van Moolenbroek "%s: dhcp6_makemessage: %m", ifp->name);
1517*9f20bfa6SDavid van Moolenbroek else
1518*9f20bfa6SDavid van Moolenbroek dhcp6_sendrebind(ifp);
1519*9f20bfa6SDavid van Moolenbroek
1520*9f20bfa6SDavid van Moolenbroek /* RFC 3633 section 12.1 */
1521*9f20bfa6SDavid van Moolenbroek if (pd)
1522*9f20bfa6SDavid van Moolenbroek eloop_timeout_add_sec(ifp->ctx->eloop,
1523*9f20bfa6SDavid van Moolenbroek CNF_MAX_RD, dhcp6_failrebind, ifp);
1524*9f20bfa6SDavid van Moolenbroek }
1525*9f20bfa6SDavid van Moolenbroek
1526*9f20bfa6SDavid van Moolenbroek
1527*9f20bfa6SDavid van Moolenbroek static void
dhcp6_startrequest(struct interface * ifp)1528*9f20bfa6SDavid van Moolenbroek dhcp6_startrequest(struct interface *ifp)
1529*9f20bfa6SDavid van Moolenbroek {
1530*9f20bfa6SDavid van Moolenbroek struct dhcp6_state *state;
1531*9f20bfa6SDavid van Moolenbroek
1532*9f20bfa6SDavid van Moolenbroek eloop_timeout_delete(ifp->ctx->eloop, dhcp6_senddiscover, ifp);
1533*9f20bfa6SDavid van Moolenbroek state = D6_STATE(ifp);
1534*9f20bfa6SDavid van Moolenbroek state->state = DH6S_REQUEST;
1535*9f20bfa6SDavid van Moolenbroek state->RTC = 0;
1536*9f20bfa6SDavid van Moolenbroek state->IRT = REQ_TIMEOUT;
1537*9f20bfa6SDavid van Moolenbroek state->MRT = REQ_MAX_RT;
1538*9f20bfa6SDavid van Moolenbroek state->MRC = REQ_MAX_RC;
1539*9f20bfa6SDavid van Moolenbroek state->MRCcallback = dhcp6_failrequest;
1540*9f20bfa6SDavid van Moolenbroek
1541*9f20bfa6SDavid van Moolenbroek if (dhcp6_makemessage(ifp) == -1) {
1542*9f20bfa6SDavid van Moolenbroek logger(ifp->ctx, LOG_ERR,
1543*9f20bfa6SDavid van Moolenbroek "%s: dhcp6_makemessage: %m", ifp->name);
1544*9f20bfa6SDavid van Moolenbroek return;
1545*9f20bfa6SDavid van Moolenbroek }
1546*9f20bfa6SDavid van Moolenbroek
1547*9f20bfa6SDavid van Moolenbroek dhcp6_sendrequest(ifp);
1548*9f20bfa6SDavid van Moolenbroek }
1549*9f20bfa6SDavid van Moolenbroek
1550*9f20bfa6SDavid van Moolenbroek static void
dhcp6_startconfirm(struct interface * ifp)1551*9f20bfa6SDavid van Moolenbroek dhcp6_startconfirm(struct interface *ifp)
1552*9f20bfa6SDavid van Moolenbroek {
1553*9f20bfa6SDavid van Moolenbroek struct dhcp6_state *state;
1554*9f20bfa6SDavid van Moolenbroek
1555*9f20bfa6SDavid van Moolenbroek state = D6_STATE(ifp);
1556*9f20bfa6SDavid van Moolenbroek state->state = DH6S_CONFIRM;
1557*9f20bfa6SDavid van Moolenbroek state->RTC = 0;
1558*9f20bfa6SDavid van Moolenbroek state->IMD = CNF_MAX_DELAY;
1559*9f20bfa6SDavid van Moolenbroek state->IRT = CNF_TIMEOUT;
1560*9f20bfa6SDavid van Moolenbroek state->MRT = CNF_MAX_RT;
1561*9f20bfa6SDavid van Moolenbroek state->MRC = 0;
1562*9f20bfa6SDavid van Moolenbroek
1563*9f20bfa6SDavid van Moolenbroek logger(ifp->ctx, LOG_INFO,
1564*9f20bfa6SDavid van Moolenbroek "%s: confirming prior DHCPv6 lease", ifp->name);
1565*9f20bfa6SDavid van Moolenbroek if (dhcp6_makemessage(ifp) == -1) {
1566*9f20bfa6SDavid van Moolenbroek logger(ifp->ctx, LOG_ERR,
1567*9f20bfa6SDavid van Moolenbroek "%s: dhcp6_makemessage: %m", ifp->name);
1568*9f20bfa6SDavid van Moolenbroek return;
1569*9f20bfa6SDavid van Moolenbroek }
1570*9f20bfa6SDavid van Moolenbroek dhcp6_sendconfirm(ifp);
1571*9f20bfa6SDavid van Moolenbroek eloop_timeout_add_sec(ifp->ctx->eloop,
1572*9f20bfa6SDavid van Moolenbroek CNF_MAX_RD, dhcp6_failconfirm, ifp);
1573*9f20bfa6SDavid van Moolenbroek }
1574*9f20bfa6SDavid van Moolenbroek
1575*9f20bfa6SDavid van Moolenbroek static void
dhcp6_startinform(void * arg)1576*9f20bfa6SDavid van Moolenbroek dhcp6_startinform(void *arg)
1577*9f20bfa6SDavid van Moolenbroek {
1578*9f20bfa6SDavid van Moolenbroek struct interface *ifp;
1579*9f20bfa6SDavid van Moolenbroek struct dhcp6_state *state;
1580*9f20bfa6SDavid van Moolenbroek
1581*9f20bfa6SDavid van Moolenbroek ifp = arg;
1582*9f20bfa6SDavid van Moolenbroek state = D6_STATE(ifp);
1583*9f20bfa6SDavid van Moolenbroek if (state->new == NULL || ifp->options->options & DHCPCD_DEBUG)
1584*9f20bfa6SDavid van Moolenbroek logger(ifp->ctx, LOG_INFO,
1585*9f20bfa6SDavid van Moolenbroek "%s: requesting DHCPv6 information", ifp->name);
1586*9f20bfa6SDavid van Moolenbroek state->state = DH6S_INFORM;
1587*9f20bfa6SDavid van Moolenbroek state->RTC = 0;
1588*9f20bfa6SDavid van Moolenbroek state->IMD = INF_MAX_DELAY;
1589*9f20bfa6SDavid van Moolenbroek state->IRT = INF_TIMEOUT;
1590*9f20bfa6SDavid van Moolenbroek state->MRT = state->inf_max_rt;
1591*9f20bfa6SDavid van Moolenbroek state->MRC = 0;
1592*9f20bfa6SDavid van Moolenbroek
1593*9f20bfa6SDavid van Moolenbroek if (dhcp6_makemessage(ifp) == -1)
1594*9f20bfa6SDavid van Moolenbroek logger(ifp->ctx, LOG_ERR,
1595*9f20bfa6SDavid van Moolenbroek "%s: dhcp6_makemessage: %m", ifp->name);
1596*9f20bfa6SDavid van Moolenbroek else
1597*9f20bfa6SDavid van Moolenbroek dhcp6_sendinform(ifp);
1598*9f20bfa6SDavid van Moolenbroek }
1599*9f20bfa6SDavid van Moolenbroek
1600*9f20bfa6SDavid van Moolenbroek static void
dhcp6_startexpire(void * arg)1601*9f20bfa6SDavid van Moolenbroek dhcp6_startexpire(void *arg)
1602*9f20bfa6SDavid van Moolenbroek {
1603*9f20bfa6SDavid van Moolenbroek struct interface *ifp;
1604*9f20bfa6SDavid van Moolenbroek
1605*9f20bfa6SDavid van Moolenbroek ifp = arg;
1606*9f20bfa6SDavid van Moolenbroek eloop_timeout_delete(ifp->ctx->eloop, dhcp6_sendrebind, ifp);
1607*9f20bfa6SDavid van Moolenbroek
1608*9f20bfa6SDavid van Moolenbroek logger(ifp->ctx, LOG_ERR, "%s: DHCPv6 lease expired", ifp->name);
1609*9f20bfa6SDavid van Moolenbroek dhcp6_freedrop_addrs(ifp, 1, NULL);
1610*9f20bfa6SDavid van Moolenbroek dhcp6_delete_delegates(ifp);
1611*9f20bfa6SDavid van Moolenbroek script_runreason(ifp, "EXPIRE6");
1612*9f20bfa6SDavid van Moolenbroek if (ipv6nd_hasradhcp(ifp) || dhcp6_hasprefixdelegation(ifp))
1613*9f20bfa6SDavid van Moolenbroek dhcp6_startdiscover(ifp);
1614*9f20bfa6SDavid van Moolenbroek else
1615*9f20bfa6SDavid van Moolenbroek logger(ifp->ctx, LOG_WARNING,
1616*9f20bfa6SDavid van Moolenbroek "%s: no advertising IPv6 router wants DHCP", ifp->name);
1617*9f20bfa6SDavid van Moolenbroek }
1618*9f20bfa6SDavid van Moolenbroek
1619*9f20bfa6SDavid van Moolenbroek static void
dhcp6_finishrelease(void * arg)1620*9f20bfa6SDavid van Moolenbroek dhcp6_finishrelease(void *arg)
1621*9f20bfa6SDavid van Moolenbroek {
1622*9f20bfa6SDavid van Moolenbroek struct interface *ifp;
1623*9f20bfa6SDavid van Moolenbroek struct dhcp6_state *state;
1624*9f20bfa6SDavid van Moolenbroek
1625*9f20bfa6SDavid van Moolenbroek ifp = (struct interface *)arg;
1626*9f20bfa6SDavid van Moolenbroek state = D6_STATE(ifp);
1627*9f20bfa6SDavid van Moolenbroek state->state = DH6S_RELEASED;
1628*9f20bfa6SDavid van Moolenbroek dhcp6_drop(ifp, "RELEASE6");
1629*9f20bfa6SDavid van Moolenbroek }
1630*9f20bfa6SDavid van Moolenbroek
1631*9f20bfa6SDavid van Moolenbroek static void
dhcp6_startrelease(struct interface * ifp)1632*9f20bfa6SDavid van Moolenbroek dhcp6_startrelease(struct interface *ifp)
1633*9f20bfa6SDavid van Moolenbroek {
1634*9f20bfa6SDavid van Moolenbroek struct dhcp6_state *state;
1635*9f20bfa6SDavid van Moolenbroek
1636*9f20bfa6SDavid van Moolenbroek state = D6_STATE(ifp);
1637*9f20bfa6SDavid van Moolenbroek if (state->state != DH6S_BOUND)
1638*9f20bfa6SDavid van Moolenbroek return;
1639*9f20bfa6SDavid van Moolenbroek
1640*9f20bfa6SDavid van Moolenbroek state->state = DH6S_RELEASE;
1641*9f20bfa6SDavid van Moolenbroek state->RTC = 0;
1642*9f20bfa6SDavid van Moolenbroek state->IRT = REL_TIMEOUT;
1643*9f20bfa6SDavid van Moolenbroek state->MRT = 0;
1644*9f20bfa6SDavid van Moolenbroek /* MRC of REL_MAX_RC is optional in RFC 3315 18.1.6 */
1645*9f20bfa6SDavid van Moolenbroek #if 0
1646*9f20bfa6SDavid van Moolenbroek state->MRC = REL_MAX_RC;
1647*9f20bfa6SDavid van Moolenbroek state->MRCcallback = dhcp6_finishrelease;
1648*9f20bfa6SDavid van Moolenbroek #else
1649*9f20bfa6SDavid van Moolenbroek state->MRC = 0;
1650*9f20bfa6SDavid van Moolenbroek state->MRCcallback = NULL;
1651*9f20bfa6SDavid van Moolenbroek #endif
1652*9f20bfa6SDavid van Moolenbroek
1653*9f20bfa6SDavid van Moolenbroek if (dhcp6_makemessage(ifp) == -1)
1654*9f20bfa6SDavid van Moolenbroek logger(ifp->ctx, LOG_ERR,
1655*9f20bfa6SDavid van Moolenbroek "%s: dhcp6_makemessage: %m", ifp->name);
1656*9f20bfa6SDavid van Moolenbroek else {
1657*9f20bfa6SDavid van Moolenbroek dhcp6_sendrelease(ifp);
1658*9f20bfa6SDavid van Moolenbroek dhcp6_finishrelease(ifp);
1659*9f20bfa6SDavid van Moolenbroek }
1660*9f20bfa6SDavid van Moolenbroek }
1661*9f20bfa6SDavid van Moolenbroek
1662*9f20bfa6SDavid van Moolenbroek static int
dhcp6_checkstatusok(const struct interface * ifp,const struct dhcp6_message * m,const uint8_t * p,size_t len)1663*9f20bfa6SDavid van Moolenbroek dhcp6_checkstatusok(const struct interface *ifp,
1664*9f20bfa6SDavid van Moolenbroek const struct dhcp6_message *m, const uint8_t *p, size_t len)
1665*9f20bfa6SDavid van Moolenbroek {
1666*9f20bfa6SDavid van Moolenbroek const struct dhcp6_option *o;
1667*9f20bfa6SDavid van Moolenbroek uint16_t code;
1668*9f20bfa6SDavid van Moolenbroek char *status;
1669*9f20bfa6SDavid van Moolenbroek
1670*9f20bfa6SDavid van Moolenbroek if (p)
1671*9f20bfa6SDavid van Moolenbroek o = dhcp6_findoption(D6_OPTION_STATUS_CODE, p, len);
1672*9f20bfa6SDavid van Moolenbroek else
1673*9f20bfa6SDavid van Moolenbroek o = dhcp6_getmoption(D6_OPTION_STATUS_CODE, m, len);
1674*9f20bfa6SDavid van Moolenbroek if (o == NULL) {
1675*9f20bfa6SDavid van Moolenbroek //logger(ifp->ctx, LOG_DEBUG, "%s: no status", ifp->name);
1676*9f20bfa6SDavid van Moolenbroek return 0;
1677*9f20bfa6SDavid van Moolenbroek }
1678*9f20bfa6SDavid van Moolenbroek
1679*9f20bfa6SDavid van Moolenbroek len = ntohs(o->len);
1680*9f20bfa6SDavid van Moolenbroek if (len < sizeof(code)) {
1681*9f20bfa6SDavid van Moolenbroek logger(ifp->ctx, LOG_ERR, "%s: status truncated", ifp->name);
1682*9f20bfa6SDavid van Moolenbroek return -1;
1683*9f20bfa6SDavid van Moolenbroek }
1684*9f20bfa6SDavid van Moolenbroek
1685*9f20bfa6SDavid van Moolenbroek p = D6_COPTION_DATA(o);
1686*9f20bfa6SDavid van Moolenbroek memcpy(&code, p, sizeof(code));
1687*9f20bfa6SDavid van Moolenbroek code = ntohs(code);
1688*9f20bfa6SDavid van Moolenbroek if (code == D6_STATUS_OK)
1689*9f20bfa6SDavid van Moolenbroek return 1;
1690*9f20bfa6SDavid van Moolenbroek
1691*9f20bfa6SDavid van Moolenbroek len -= sizeof(code);
1692*9f20bfa6SDavid van Moolenbroek
1693*9f20bfa6SDavid van Moolenbroek if (len == 0) {
1694*9f20bfa6SDavid van Moolenbroek if (code < sizeof(dhcp6_statuses) / sizeof(char *)) {
1695*9f20bfa6SDavid van Moolenbroek p = (const uint8_t *)dhcp6_statuses[code];
1696*9f20bfa6SDavid van Moolenbroek len = strlen((const char *)p);
1697*9f20bfa6SDavid van Moolenbroek } else
1698*9f20bfa6SDavid van Moolenbroek p = NULL;
1699*9f20bfa6SDavid van Moolenbroek } else
1700*9f20bfa6SDavid van Moolenbroek p += sizeof(code);
1701*9f20bfa6SDavid van Moolenbroek
1702*9f20bfa6SDavid van Moolenbroek status = malloc(len + 1);
1703*9f20bfa6SDavid van Moolenbroek if (status == NULL) {
1704*9f20bfa6SDavid van Moolenbroek logger(ifp->ctx, LOG_ERR, "%s: %m", __func__);
1705*9f20bfa6SDavid van Moolenbroek return -1;
1706*9f20bfa6SDavid van Moolenbroek }
1707*9f20bfa6SDavid van Moolenbroek if (p)
1708*9f20bfa6SDavid van Moolenbroek memcpy(status, p, len);
1709*9f20bfa6SDavid van Moolenbroek status[len] = '\0';
1710*9f20bfa6SDavid van Moolenbroek logger(ifp->ctx, LOG_ERR, "%s: DHCPv6 REPLY: %s", ifp->name, status);
1711*9f20bfa6SDavid van Moolenbroek free(status);
1712*9f20bfa6SDavid van Moolenbroek return -1;
1713*9f20bfa6SDavid van Moolenbroek }
1714*9f20bfa6SDavid van Moolenbroek
1715*9f20bfa6SDavid van Moolenbroek const struct ipv6_addr *
dhcp6_iffindaddr(const struct interface * ifp,const struct in6_addr * addr,short flags)1716*9f20bfa6SDavid van Moolenbroek dhcp6_iffindaddr(const struct interface *ifp, const struct in6_addr *addr,
1717*9f20bfa6SDavid van Moolenbroek short flags)
1718*9f20bfa6SDavid van Moolenbroek {
1719*9f20bfa6SDavid van Moolenbroek const struct dhcp6_state *state;
1720*9f20bfa6SDavid van Moolenbroek const struct ipv6_addr *ap;
1721*9f20bfa6SDavid van Moolenbroek
1722*9f20bfa6SDavid van Moolenbroek if ((state = D6_STATE(ifp)) != NULL) {
1723*9f20bfa6SDavid van Moolenbroek TAILQ_FOREACH(ap, &state->addrs, next) {
1724*9f20bfa6SDavid van Moolenbroek if (ipv6_findaddrmatch(ap, addr, flags))
1725*9f20bfa6SDavid van Moolenbroek return ap;
1726*9f20bfa6SDavid van Moolenbroek }
1727*9f20bfa6SDavid van Moolenbroek }
1728*9f20bfa6SDavid van Moolenbroek return NULL;
1729*9f20bfa6SDavid van Moolenbroek }
1730*9f20bfa6SDavid van Moolenbroek
1731*9f20bfa6SDavid van Moolenbroek struct ipv6_addr *
dhcp6_findaddr(struct dhcpcd_ctx * ctx,const struct in6_addr * addr,short flags)1732*9f20bfa6SDavid van Moolenbroek dhcp6_findaddr(struct dhcpcd_ctx *ctx, const struct in6_addr *addr,
1733*9f20bfa6SDavid van Moolenbroek short flags)
1734*9f20bfa6SDavid van Moolenbroek {
1735*9f20bfa6SDavid van Moolenbroek struct interface *ifp;
1736*9f20bfa6SDavid van Moolenbroek struct ipv6_addr *ap;
1737*9f20bfa6SDavid van Moolenbroek struct dhcp6_state *state;
1738*9f20bfa6SDavid van Moolenbroek
1739*9f20bfa6SDavid van Moolenbroek TAILQ_FOREACH(ifp, ctx->ifaces, next) {
1740*9f20bfa6SDavid van Moolenbroek if ((state = D6_STATE(ifp)) != NULL) {
1741*9f20bfa6SDavid van Moolenbroek TAILQ_FOREACH(ap, &state->addrs, next) {
1742*9f20bfa6SDavid van Moolenbroek if (ipv6_findaddrmatch(ap, addr, flags))
1743*9f20bfa6SDavid van Moolenbroek return ap;
1744*9f20bfa6SDavid van Moolenbroek }
1745*9f20bfa6SDavid van Moolenbroek }
1746*9f20bfa6SDavid van Moolenbroek }
1747*9f20bfa6SDavid van Moolenbroek return NULL;
1748*9f20bfa6SDavid van Moolenbroek }
1749*9f20bfa6SDavid van Moolenbroek
1750*9f20bfa6SDavid van Moolenbroek static int
dhcp6_findna(struct interface * ifp,uint16_t ot,const uint8_t * iaid,const uint8_t * d,size_t l,const struct timespec * acquired)1751*9f20bfa6SDavid van Moolenbroek dhcp6_findna(struct interface *ifp, uint16_t ot, const uint8_t *iaid,
1752*9f20bfa6SDavid van Moolenbroek const uint8_t *d, size_t l, const struct timespec *acquired)
1753*9f20bfa6SDavid van Moolenbroek {
1754*9f20bfa6SDavid van Moolenbroek struct dhcp6_state *state;
1755*9f20bfa6SDavid van Moolenbroek const struct dhcp6_option *o;
1756*9f20bfa6SDavid van Moolenbroek struct ipv6_addr *a;
1757*9f20bfa6SDavid van Moolenbroek char iabuf[INET6_ADDRSTRLEN];
1758*9f20bfa6SDavid van Moolenbroek const char *ia;
1759*9f20bfa6SDavid van Moolenbroek int i;
1760*9f20bfa6SDavid van Moolenbroek uint32_t u32;
1761*9f20bfa6SDavid van Moolenbroek size_t off;
1762*9f20bfa6SDavid van Moolenbroek const struct dhcp6_ia_addr *iap;
1763*9f20bfa6SDavid van Moolenbroek
1764*9f20bfa6SDavid van Moolenbroek i = 0;
1765*9f20bfa6SDavid van Moolenbroek state = D6_STATE(ifp);
1766*9f20bfa6SDavid van Moolenbroek while ((o = dhcp6_findoption(D6_OPTION_IA_ADDR, d, l))) {
1767*9f20bfa6SDavid van Moolenbroek off = (size_t)((const uint8_t *)o - d);
1768*9f20bfa6SDavid van Moolenbroek l -= off;
1769*9f20bfa6SDavid van Moolenbroek d += off;
1770*9f20bfa6SDavid van Moolenbroek u32 = ntohs(o->len);
1771*9f20bfa6SDavid van Moolenbroek l -= sizeof(*o) + u32;
1772*9f20bfa6SDavid van Moolenbroek d += sizeof(*o) + u32;
1773*9f20bfa6SDavid van Moolenbroek if (u32 < 24) {
1774*9f20bfa6SDavid van Moolenbroek errno = EINVAL;
1775*9f20bfa6SDavid van Moolenbroek logger(ifp->ctx, LOG_ERR,
1776*9f20bfa6SDavid van Moolenbroek "%s: IA Address option truncated", ifp->name);
1777*9f20bfa6SDavid van Moolenbroek continue;
1778*9f20bfa6SDavid van Moolenbroek }
1779*9f20bfa6SDavid van Moolenbroek iap = (const struct dhcp6_ia_addr *)D6_COPTION_DATA(o);
1780*9f20bfa6SDavid van Moolenbroek TAILQ_FOREACH(a, &state->addrs, next) {
1781*9f20bfa6SDavid van Moolenbroek if (ipv6_findaddrmatch(a, &iap->addr, 0))
1782*9f20bfa6SDavid van Moolenbroek break;
1783*9f20bfa6SDavid van Moolenbroek }
1784*9f20bfa6SDavid van Moolenbroek if (a == NULL) {
1785*9f20bfa6SDavid van Moolenbroek a = calloc(1, sizeof(*a));
1786*9f20bfa6SDavid van Moolenbroek if (a == NULL) {
1787*9f20bfa6SDavid van Moolenbroek logger(ifp->ctx, LOG_ERR, "%s: %m", __func__);
1788*9f20bfa6SDavid van Moolenbroek break;
1789*9f20bfa6SDavid van Moolenbroek }
1790*9f20bfa6SDavid van Moolenbroek a->iface = ifp;
1791*9f20bfa6SDavid van Moolenbroek a->flags = IPV6_AF_NEW | IPV6_AF_ONLINK;
1792*9f20bfa6SDavid van Moolenbroek a->dadcallback = dhcp6_dadcallback;
1793*9f20bfa6SDavid van Moolenbroek a->ia_type = ot;
1794*9f20bfa6SDavid van Moolenbroek memcpy(a->iaid, iaid, sizeof(a->iaid));
1795*9f20bfa6SDavid van Moolenbroek a->addr = iap->addr;
1796*9f20bfa6SDavid van Moolenbroek a->created = *acquired;
1797*9f20bfa6SDavid van Moolenbroek
1798*9f20bfa6SDavid van Moolenbroek /*
1799*9f20bfa6SDavid van Moolenbroek * RFC 5942 Section 5
1800*9f20bfa6SDavid van Moolenbroek * We cannot assume any prefix length, nor tie the
1801*9f20bfa6SDavid van Moolenbroek * address to an existing one as it could expire
1802*9f20bfa6SDavid van Moolenbroek * before the address.
1803*9f20bfa6SDavid van Moolenbroek * As such we just give it a 128 prefix.
1804*9f20bfa6SDavid van Moolenbroek */
1805*9f20bfa6SDavid van Moolenbroek a->prefix_len = 128;
1806*9f20bfa6SDavid van Moolenbroek ipv6_makeprefix(&a->prefix, &a->addr, a->prefix_len);
1807*9f20bfa6SDavid van Moolenbroek ia = inet_ntop(AF_INET6, &a->addr,
1808*9f20bfa6SDavid van Moolenbroek iabuf, sizeof(iabuf));
1809*9f20bfa6SDavid van Moolenbroek snprintf(a->saddr, sizeof(a->saddr),
1810*9f20bfa6SDavid van Moolenbroek "%s/%d", ia, a->prefix_len);
1811*9f20bfa6SDavid van Moolenbroek
1812*9f20bfa6SDavid van Moolenbroek TAILQ_INSERT_TAIL(&state->addrs, a, next);
1813*9f20bfa6SDavid van Moolenbroek } else {
1814*9f20bfa6SDavid van Moolenbroek if (!(a->flags & IPV6_AF_ONLINK))
1815*9f20bfa6SDavid van Moolenbroek a->flags |= IPV6_AF_ONLINK | IPV6_AF_NEW;
1816*9f20bfa6SDavid van Moolenbroek a->flags &= ~IPV6_AF_STALE;
1817*9f20bfa6SDavid van Moolenbroek }
1818*9f20bfa6SDavid van Moolenbroek a->acquired = *acquired;
1819*9f20bfa6SDavid van Moolenbroek a->prefix_pltime = ntohl(iap->pltime);
1820*9f20bfa6SDavid van Moolenbroek u32 = ntohl(iap->vltime);
1821*9f20bfa6SDavid van Moolenbroek if (a->prefix_vltime != u32) {
1822*9f20bfa6SDavid van Moolenbroek a->flags |= IPV6_AF_NEW;
1823*9f20bfa6SDavid van Moolenbroek a->prefix_vltime = u32;
1824*9f20bfa6SDavid van Moolenbroek }
1825*9f20bfa6SDavid van Moolenbroek if (a->prefix_pltime && a->prefix_pltime < state->lowpl)
1826*9f20bfa6SDavid van Moolenbroek state->lowpl = a->prefix_pltime;
1827*9f20bfa6SDavid van Moolenbroek if (a->prefix_vltime && a->prefix_vltime > state->expire)
1828*9f20bfa6SDavid van Moolenbroek state->expire = a->prefix_vltime;
1829*9f20bfa6SDavid van Moolenbroek i++;
1830*9f20bfa6SDavid van Moolenbroek }
1831*9f20bfa6SDavid van Moolenbroek return i;
1832*9f20bfa6SDavid van Moolenbroek }
1833*9f20bfa6SDavid van Moolenbroek
1834*9f20bfa6SDavid van Moolenbroek static int
dhcp6_findpd(struct interface * ifp,const uint8_t * iaid,const uint8_t * d,size_t l,const struct timespec * acquired)1835*9f20bfa6SDavid van Moolenbroek dhcp6_findpd(struct interface *ifp, const uint8_t *iaid,
1836*9f20bfa6SDavid van Moolenbroek const uint8_t *d, size_t l, const struct timespec *acquired)
1837*9f20bfa6SDavid van Moolenbroek {
1838*9f20bfa6SDavid van Moolenbroek struct dhcp6_state *state;
1839*9f20bfa6SDavid van Moolenbroek const struct dhcp6_option *o, *ex;
1840*9f20bfa6SDavid van Moolenbroek const uint8_t *p, *op;
1841*9f20bfa6SDavid van Moolenbroek struct ipv6_addr *a;
1842*9f20bfa6SDavid van Moolenbroek char iabuf[INET6_ADDRSTRLEN];
1843*9f20bfa6SDavid van Moolenbroek const char *ia;
1844*9f20bfa6SDavid van Moolenbroek int i;
1845*9f20bfa6SDavid van Moolenbroek uint8_t u8, *pw;
1846*9f20bfa6SDavid van Moolenbroek size_t off;
1847*9f20bfa6SDavid van Moolenbroek uint16_t ol;
1848*9f20bfa6SDavid van Moolenbroek const struct dhcp6_pd_addr *pdp;
1849*9f20bfa6SDavid van Moolenbroek
1850*9f20bfa6SDavid van Moolenbroek i = 0;
1851*9f20bfa6SDavid van Moolenbroek state = D6_STATE(ifp);
1852*9f20bfa6SDavid van Moolenbroek while ((o = dhcp6_findoption(D6_OPTION_IAPREFIX, d, l))) {
1853*9f20bfa6SDavid van Moolenbroek off = (size_t)((const uint8_t *)o - d);
1854*9f20bfa6SDavid van Moolenbroek l -= off;
1855*9f20bfa6SDavid van Moolenbroek d += off;
1856*9f20bfa6SDavid van Moolenbroek ol = ntohs(o->len);
1857*9f20bfa6SDavid van Moolenbroek l -= sizeof(*o) + ol;
1858*9f20bfa6SDavid van Moolenbroek d += sizeof(*o) + ol;
1859*9f20bfa6SDavid van Moolenbroek if (ol < sizeof(*pdp)) {
1860*9f20bfa6SDavid van Moolenbroek errno = EINVAL;
1861*9f20bfa6SDavid van Moolenbroek logger(ifp->ctx, LOG_ERR,
1862*9f20bfa6SDavid van Moolenbroek "%s: IA Prefix option truncated", ifp->name);
1863*9f20bfa6SDavid van Moolenbroek continue;
1864*9f20bfa6SDavid van Moolenbroek }
1865*9f20bfa6SDavid van Moolenbroek
1866*9f20bfa6SDavid van Moolenbroek pdp = (const struct dhcp6_pd_addr *)D6_COPTION_DATA(o);
1867*9f20bfa6SDavid van Moolenbroek TAILQ_FOREACH(a, &state->addrs, next) {
1868*9f20bfa6SDavid van Moolenbroek if (IN6_ARE_ADDR_EQUAL(&a->prefix, &pdp->prefix))
1869*9f20bfa6SDavid van Moolenbroek break;
1870*9f20bfa6SDavid van Moolenbroek }
1871*9f20bfa6SDavid van Moolenbroek if (a == NULL) {
1872*9f20bfa6SDavid van Moolenbroek a = calloc(1, sizeof(*a));
1873*9f20bfa6SDavid van Moolenbroek if (a == NULL) {
1874*9f20bfa6SDavid van Moolenbroek logger(ifp->ctx, LOG_ERR, "%s: %m", __func__);
1875*9f20bfa6SDavid van Moolenbroek break;
1876*9f20bfa6SDavid van Moolenbroek }
1877*9f20bfa6SDavid van Moolenbroek a->iface = ifp;
1878*9f20bfa6SDavid van Moolenbroek a->flags = IPV6_AF_NEW | IPV6_AF_DELEGATEDPFX;
1879*9f20bfa6SDavid van Moolenbroek a->created = *acquired;
1880*9f20bfa6SDavid van Moolenbroek a->dadcallback = dhcp6_dadcallback;
1881*9f20bfa6SDavid van Moolenbroek a->ia_type = D6_OPTION_IA_PD;
1882*9f20bfa6SDavid van Moolenbroek memcpy(a->iaid, iaid, sizeof(a->iaid));
1883*9f20bfa6SDavid van Moolenbroek a->prefix = pdp->prefix;
1884*9f20bfa6SDavid van Moolenbroek a->prefix_len = pdp->prefix_len;
1885*9f20bfa6SDavid van Moolenbroek ia = inet_ntop(AF_INET6, &a->prefix,
1886*9f20bfa6SDavid van Moolenbroek iabuf, sizeof(iabuf));
1887*9f20bfa6SDavid van Moolenbroek snprintf(a->saddr, sizeof(a->saddr),
1888*9f20bfa6SDavid van Moolenbroek "%s/%d", ia, a->prefix_len);
1889*9f20bfa6SDavid van Moolenbroek TAILQ_INSERT_TAIL(&state->addrs, a, next);
1890*9f20bfa6SDavid van Moolenbroek } else {
1891*9f20bfa6SDavid van Moolenbroek if (!(a->flags & IPV6_AF_DELEGATEDPFX))
1892*9f20bfa6SDavid van Moolenbroek a->flags |= IPV6_AF_NEW | IPV6_AF_DELEGATEDPFX;
1893*9f20bfa6SDavid van Moolenbroek a->flags &= ~(IPV6_AF_STALE | IPV6_AF_REQUEST);
1894*9f20bfa6SDavid van Moolenbroek if (a->prefix_vltime != ntohl(pdp->vltime))
1895*9f20bfa6SDavid van Moolenbroek a->flags |= IPV6_AF_NEW;
1896*9f20bfa6SDavid van Moolenbroek }
1897*9f20bfa6SDavid van Moolenbroek
1898*9f20bfa6SDavid van Moolenbroek a->acquired = *acquired;
1899*9f20bfa6SDavid van Moolenbroek a->prefix_pltime = ntohl(pdp->pltime);
1900*9f20bfa6SDavid van Moolenbroek a->prefix_vltime = ntohl(pdp->vltime);
1901*9f20bfa6SDavid van Moolenbroek
1902*9f20bfa6SDavid van Moolenbroek if (a->prefix_pltime && a->prefix_pltime < state->lowpl)
1903*9f20bfa6SDavid van Moolenbroek state->lowpl = a->prefix_pltime;
1904*9f20bfa6SDavid van Moolenbroek if (a->prefix_vltime && a->prefix_vltime > state->expire)
1905*9f20bfa6SDavid van Moolenbroek state->expire = a->prefix_vltime;
1906*9f20bfa6SDavid van Moolenbroek i++;
1907*9f20bfa6SDavid van Moolenbroek
1908*9f20bfa6SDavid van Moolenbroek p = D6_COPTION_DATA(o) + sizeof(pdp);
1909*9f20bfa6SDavid van Moolenbroek ol = (uint16_t)(ol - sizeof(pdp));
1910*9f20bfa6SDavid van Moolenbroek ex = dhcp6_findoption(D6_OPTION_PD_EXCLUDE, p, ol);
1911*9f20bfa6SDavid van Moolenbroek a->prefix_exclude_len = 0;
1912*9f20bfa6SDavid van Moolenbroek memset(&a->prefix_exclude, 0, sizeof(a->prefix_exclude));
1913*9f20bfa6SDavid van Moolenbroek #if 0
1914*9f20bfa6SDavid van Moolenbroek if (ex == NULL) {
1915*9f20bfa6SDavid van Moolenbroek struct dhcp6_option *w;
1916*9f20bfa6SDavid van Moolenbroek uint8_t *wp;
1917*9f20bfa6SDavid van Moolenbroek
1918*9f20bfa6SDavid van Moolenbroek w = calloc(1, 128);
1919*9f20bfa6SDavid van Moolenbroek w->len = htons(2);
1920*9f20bfa6SDavid van Moolenbroek wp = D6_OPTION_DATA(w);
1921*9f20bfa6SDavid van Moolenbroek *wp++ = 64;
1922*9f20bfa6SDavid van Moolenbroek *wp++ = 0x78;
1923*9f20bfa6SDavid van Moolenbroek ex = w;
1924*9f20bfa6SDavid van Moolenbroek }
1925*9f20bfa6SDavid van Moolenbroek #endif
1926*9f20bfa6SDavid van Moolenbroek if (ex == NULL)
1927*9f20bfa6SDavid van Moolenbroek continue;
1928*9f20bfa6SDavid van Moolenbroek ol = ntohs(ex->len);
1929*9f20bfa6SDavid van Moolenbroek if (ol < 2) {
1930*9f20bfa6SDavid van Moolenbroek logger(ifp->ctx, LOG_ERR,
1931*9f20bfa6SDavid van Moolenbroek "%s: truncated PD Exclude", ifp->name);
1932*9f20bfa6SDavid van Moolenbroek continue;
1933*9f20bfa6SDavid van Moolenbroek }
1934*9f20bfa6SDavid van Moolenbroek op = D6_COPTION_DATA(ex);
1935*9f20bfa6SDavid van Moolenbroek a->prefix_exclude_len = *op++;
1936*9f20bfa6SDavid van Moolenbroek ol--;
1937*9f20bfa6SDavid van Moolenbroek if (((a->prefix_exclude_len - a->prefix_len - 1) / NBBY) + 1
1938*9f20bfa6SDavid van Moolenbroek != ol)
1939*9f20bfa6SDavid van Moolenbroek {
1940*9f20bfa6SDavid van Moolenbroek logger(ifp->ctx, LOG_ERR,
1941*9f20bfa6SDavid van Moolenbroek "%s: PD Exclude length mismatch", ifp->name);
1942*9f20bfa6SDavid van Moolenbroek a->prefix_exclude_len = 0;
1943*9f20bfa6SDavid van Moolenbroek continue;
1944*9f20bfa6SDavid van Moolenbroek }
1945*9f20bfa6SDavid van Moolenbroek u8 = a->prefix_len % NBBY;
1946*9f20bfa6SDavid van Moolenbroek memcpy(&a->prefix_exclude, &a->prefix,
1947*9f20bfa6SDavid van Moolenbroek sizeof(a->prefix_exclude));
1948*9f20bfa6SDavid van Moolenbroek if (u8)
1949*9f20bfa6SDavid van Moolenbroek ol--;
1950*9f20bfa6SDavid van Moolenbroek pw = a->prefix_exclude.s6_addr +
1951*9f20bfa6SDavid van Moolenbroek (a->prefix_exclude_len / NBBY) - 1;
1952*9f20bfa6SDavid van Moolenbroek while (ol-- > 0)
1953*9f20bfa6SDavid van Moolenbroek *pw-- = *op++;
1954*9f20bfa6SDavid van Moolenbroek if (u8)
1955*9f20bfa6SDavid van Moolenbroek *pw = (uint8_t)(*pw | (*op >> u8));
1956*9f20bfa6SDavid van Moolenbroek }
1957*9f20bfa6SDavid van Moolenbroek return i;
1958*9f20bfa6SDavid van Moolenbroek }
1959*9f20bfa6SDavid van Moolenbroek
1960*9f20bfa6SDavid van Moolenbroek static int
dhcp6_findia(struct interface * ifp,const struct dhcp6_message * m,size_t l,const char * sfrom,const struct timespec * acquired)1961*9f20bfa6SDavid van Moolenbroek dhcp6_findia(struct interface *ifp, const struct dhcp6_message *m, size_t l,
1962*9f20bfa6SDavid van Moolenbroek const char *sfrom, const struct timespec *acquired)
1963*9f20bfa6SDavid van Moolenbroek {
1964*9f20bfa6SDavid van Moolenbroek struct dhcp6_state *state;
1965*9f20bfa6SDavid van Moolenbroek const struct if_options *ifo;
1966*9f20bfa6SDavid van Moolenbroek const struct dhcp6_option *o;
1967*9f20bfa6SDavid van Moolenbroek const uint8_t *p;
1968*9f20bfa6SDavid van Moolenbroek int i, e;
1969*9f20bfa6SDavid van Moolenbroek size_t j;
1970*9f20bfa6SDavid van Moolenbroek uint32_t u32, renew, rebind;
1971*9f20bfa6SDavid van Moolenbroek uint16_t code, ol;
1972*9f20bfa6SDavid van Moolenbroek uint8_t iaid[4];
1973*9f20bfa6SDavid van Moolenbroek char buf[sizeof(iaid) * 3];
1974*9f20bfa6SDavid van Moolenbroek struct ipv6_addr *ap, *nap;
1975*9f20bfa6SDavid van Moolenbroek
1976*9f20bfa6SDavid van Moolenbroek if (l < sizeof(*m)) {
1977*9f20bfa6SDavid van Moolenbroek /* Should be impossible with guards at packet in
1978*9f20bfa6SDavid van Moolenbroek * and reading leases */
1979*9f20bfa6SDavid van Moolenbroek errno = EINVAL;
1980*9f20bfa6SDavid van Moolenbroek return -1;
1981*9f20bfa6SDavid van Moolenbroek }
1982*9f20bfa6SDavid van Moolenbroek
1983*9f20bfa6SDavid van Moolenbroek ifo = ifp->options;
1984*9f20bfa6SDavid van Moolenbroek i = e = 0;
1985*9f20bfa6SDavid van Moolenbroek state = D6_STATE(ifp);
1986*9f20bfa6SDavid van Moolenbroek TAILQ_FOREACH(ap, &state->addrs, next) {
1987*9f20bfa6SDavid van Moolenbroek ap->flags |= IPV6_AF_STALE;
1988*9f20bfa6SDavid van Moolenbroek }
1989*9f20bfa6SDavid van Moolenbroek l -= sizeof(*m);
1990*9f20bfa6SDavid van Moolenbroek for (o = D6_CFIRST_OPTION(m); l > sizeof(*o); o = D6_CNEXT_OPTION(o)) {
1991*9f20bfa6SDavid van Moolenbroek ol = ntohs(o->len);
1992*9f20bfa6SDavid van Moolenbroek if (sizeof(*o) + ol > l) {
1993*9f20bfa6SDavid van Moolenbroek errno = EINVAL;
1994*9f20bfa6SDavid van Moolenbroek logger(ifp->ctx, LOG_ERR,
1995*9f20bfa6SDavid van Moolenbroek "%s: option overflow", ifp->name);
1996*9f20bfa6SDavid van Moolenbroek break;
1997*9f20bfa6SDavid van Moolenbroek }
1998*9f20bfa6SDavid van Moolenbroek l -= sizeof(*o) + ol;
1999*9f20bfa6SDavid van Moolenbroek
2000*9f20bfa6SDavid van Moolenbroek code = ntohs(o->code);
2001*9f20bfa6SDavid van Moolenbroek switch(code) {
2002*9f20bfa6SDavid van Moolenbroek case D6_OPTION_IA_TA:
2003*9f20bfa6SDavid van Moolenbroek u32 = 4;
2004*9f20bfa6SDavid van Moolenbroek break;
2005*9f20bfa6SDavid van Moolenbroek case D6_OPTION_IA_NA:
2006*9f20bfa6SDavid van Moolenbroek case D6_OPTION_IA_PD:
2007*9f20bfa6SDavid van Moolenbroek u32 = 12;
2008*9f20bfa6SDavid van Moolenbroek break;
2009*9f20bfa6SDavid van Moolenbroek default:
2010*9f20bfa6SDavid van Moolenbroek continue;
2011*9f20bfa6SDavid van Moolenbroek }
2012*9f20bfa6SDavid van Moolenbroek if (ol < u32) {
2013*9f20bfa6SDavid van Moolenbroek errno = EINVAL;
2014*9f20bfa6SDavid van Moolenbroek logger(ifp->ctx, LOG_ERR,
2015*9f20bfa6SDavid van Moolenbroek "%s: IA option truncated", ifp->name);
2016*9f20bfa6SDavid van Moolenbroek continue;
2017*9f20bfa6SDavid van Moolenbroek }
2018*9f20bfa6SDavid van Moolenbroek
2019*9f20bfa6SDavid van Moolenbroek p = D6_COPTION_DATA(o);
2020*9f20bfa6SDavid van Moolenbroek memcpy(iaid, p, sizeof(iaid));
2021*9f20bfa6SDavid van Moolenbroek p += sizeof(iaid);
2022*9f20bfa6SDavid van Moolenbroek ol = (uint16_t)(ol - sizeof(iaid));
2023*9f20bfa6SDavid van Moolenbroek
2024*9f20bfa6SDavid van Moolenbroek for (j = 0; j < ifo->ia_len; j++) {
2025*9f20bfa6SDavid van Moolenbroek if (memcmp(&ifo->ia[j].iaid, iaid, sizeof(iaid)) == 0)
2026*9f20bfa6SDavid van Moolenbroek break;
2027*9f20bfa6SDavid van Moolenbroek }
2028*9f20bfa6SDavid van Moolenbroek if (j == ifo->ia_len &&
2029*9f20bfa6SDavid van Moolenbroek !(ifo->ia_len == 0 && ifp->ctx->options & DHCPCD_DUMPLEASE))
2030*9f20bfa6SDavid van Moolenbroek {
2031*9f20bfa6SDavid van Moolenbroek logger(ifp->ctx, LOG_DEBUG,
2032*9f20bfa6SDavid van Moolenbroek "%s: ignoring unrequested IAID %s",
2033*9f20bfa6SDavid van Moolenbroek ifp->name,
2034*9f20bfa6SDavid van Moolenbroek hwaddr_ntoa(iaid, sizeof(iaid), buf, sizeof(buf)));
2035*9f20bfa6SDavid van Moolenbroek continue;
2036*9f20bfa6SDavid van Moolenbroek }
2037*9f20bfa6SDavid van Moolenbroek if ( j < ifo->ia_len && ifo->ia[j].ia_type != code) {
2038*9f20bfa6SDavid van Moolenbroek logger(ifp->ctx, LOG_ERR,
2039*9f20bfa6SDavid van Moolenbroek "%s: IAID %s: option type mismatch",
2040*9f20bfa6SDavid van Moolenbroek ifp->name,
2041*9f20bfa6SDavid van Moolenbroek hwaddr_ntoa(iaid, sizeof(iaid), buf, sizeof(buf)));
2042*9f20bfa6SDavid van Moolenbroek continue;
2043*9f20bfa6SDavid van Moolenbroek }
2044*9f20bfa6SDavid van Moolenbroek
2045*9f20bfa6SDavid van Moolenbroek if (code != D6_OPTION_IA_TA) {
2046*9f20bfa6SDavid van Moolenbroek memcpy(&u32, p, sizeof(u32));
2047*9f20bfa6SDavid van Moolenbroek renew = ntohl(u32);
2048*9f20bfa6SDavid van Moolenbroek p += sizeof(u32);
2049*9f20bfa6SDavid van Moolenbroek ol = (uint16_t)(ol - sizeof(u32));
2050*9f20bfa6SDavid van Moolenbroek memcpy(&u32, p, sizeof(u32));
2051*9f20bfa6SDavid van Moolenbroek rebind = ntohl(u32);
2052*9f20bfa6SDavid van Moolenbroek p += sizeof(u32);
2053*9f20bfa6SDavid van Moolenbroek ol = (uint16_t)(ol - sizeof(u32));
2054*9f20bfa6SDavid van Moolenbroek } else
2055*9f20bfa6SDavid van Moolenbroek renew = rebind = 0; /* appease gcc */
2056*9f20bfa6SDavid van Moolenbroek if (dhcp6_checkstatusok(ifp, NULL, p, ol) == -1) {
2057*9f20bfa6SDavid van Moolenbroek e = 1;
2058*9f20bfa6SDavid van Moolenbroek continue;
2059*9f20bfa6SDavid van Moolenbroek }
2060*9f20bfa6SDavid van Moolenbroek if (code == D6_OPTION_IA_PD) {
2061*9f20bfa6SDavid van Moolenbroek if (dhcp6_findpd(ifp, iaid, p, ol, acquired) == 0) {
2062*9f20bfa6SDavid van Moolenbroek logger(ifp->ctx, LOG_WARNING,
2063*9f20bfa6SDavid van Moolenbroek "%s: %s: DHCPv6 REPLY missing Prefix",
2064*9f20bfa6SDavid van Moolenbroek ifp->name, sfrom);
2065*9f20bfa6SDavid van Moolenbroek continue;
2066*9f20bfa6SDavid van Moolenbroek }
2067*9f20bfa6SDavid van Moolenbroek } else {
2068*9f20bfa6SDavid van Moolenbroek if (dhcp6_findna(ifp, code, iaid, p, ol, acquired) == 0)
2069*9f20bfa6SDavid van Moolenbroek {
2070*9f20bfa6SDavid van Moolenbroek logger(ifp->ctx, LOG_WARNING,
2071*9f20bfa6SDavid van Moolenbroek "%s: %s: DHCPv6 REPLY missing IA Address",
2072*9f20bfa6SDavid van Moolenbroek ifp->name, sfrom);
2073*9f20bfa6SDavid van Moolenbroek continue;
2074*9f20bfa6SDavid van Moolenbroek }
2075*9f20bfa6SDavid van Moolenbroek }
2076*9f20bfa6SDavid van Moolenbroek if (code != D6_OPTION_IA_TA) {
2077*9f20bfa6SDavid van Moolenbroek if (renew > rebind && rebind > 0) {
2078*9f20bfa6SDavid van Moolenbroek if (sfrom)
2079*9f20bfa6SDavid van Moolenbroek logger(ifp->ctx, LOG_WARNING,
2080*9f20bfa6SDavid van Moolenbroek "%s: T1 (%d) > T2 (%d) from %s",
2081*9f20bfa6SDavid van Moolenbroek ifp->name, renew, rebind, sfrom);
2082*9f20bfa6SDavid van Moolenbroek renew = 0;
2083*9f20bfa6SDavid van Moolenbroek rebind = 0;
2084*9f20bfa6SDavid van Moolenbroek }
2085*9f20bfa6SDavid van Moolenbroek if (renew != 0 &&
2086*9f20bfa6SDavid van Moolenbroek (renew < state->renew || state->renew == 0))
2087*9f20bfa6SDavid van Moolenbroek state->renew = renew;
2088*9f20bfa6SDavid van Moolenbroek if (rebind != 0 &&
2089*9f20bfa6SDavid van Moolenbroek (rebind < state->rebind || state->rebind == 0))
2090*9f20bfa6SDavid van Moolenbroek state->rebind = rebind;
2091*9f20bfa6SDavid van Moolenbroek }
2092*9f20bfa6SDavid van Moolenbroek i++;
2093*9f20bfa6SDavid van Moolenbroek }
2094*9f20bfa6SDavid van Moolenbroek TAILQ_FOREACH_SAFE(ap, &state->addrs, next, nap) {
2095*9f20bfa6SDavid van Moolenbroek if (ap->flags & IPV6_AF_STALE) {
2096*9f20bfa6SDavid van Moolenbroek eloop_q_timeout_delete(ifp->ctx->eloop, 0, NULL, ap);
2097*9f20bfa6SDavid van Moolenbroek if (ap->flags & IPV6_AF_REQUEST) {
2098*9f20bfa6SDavid van Moolenbroek ap->prefix_vltime = ap->prefix_pltime = 0;
2099*9f20bfa6SDavid van Moolenbroek } else {
2100*9f20bfa6SDavid van Moolenbroek TAILQ_REMOVE(&state->addrs, ap, next);
2101*9f20bfa6SDavid van Moolenbroek free(ap);
2102*9f20bfa6SDavid van Moolenbroek }
2103*9f20bfa6SDavid van Moolenbroek }
2104*9f20bfa6SDavid van Moolenbroek }
2105*9f20bfa6SDavid van Moolenbroek if (i == 0 && e)
2106*9f20bfa6SDavid van Moolenbroek return -1;
2107*9f20bfa6SDavid van Moolenbroek return i;
2108*9f20bfa6SDavid van Moolenbroek }
2109*9f20bfa6SDavid van Moolenbroek
2110*9f20bfa6SDavid van Moolenbroek static int
dhcp6_validatelease(struct interface * ifp,const struct dhcp6_message * m,size_t len,const char * sfrom,const struct timespec * acquired)2111*9f20bfa6SDavid van Moolenbroek dhcp6_validatelease(struct interface *ifp,
2112*9f20bfa6SDavid van Moolenbroek const struct dhcp6_message *m, size_t len,
2113*9f20bfa6SDavid van Moolenbroek const char *sfrom, const struct timespec *acquired)
2114*9f20bfa6SDavid van Moolenbroek {
2115*9f20bfa6SDavid van Moolenbroek struct dhcp6_state *state;
2116*9f20bfa6SDavid van Moolenbroek int nia;
2117*9f20bfa6SDavid van Moolenbroek struct timespec aq;
2118*9f20bfa6SDavid van Moolenbroek
2119*9f20bfa6SDavid van Moolenbroek if (len <= sizeof(*m)) {
2120*9f20bfa6SDavid van Moolenbroek logger(ifp->ctx, LOG_ERR,
2121*9f20bfa6SDavid van Moolenbroek "%s: DHCPv6 lease truncated", ifp->name);
2122*9f20bfa6SDavid van Moolenbroek return -1;
2123*9f20bfa6SDavid van Moolenbroek }
2124*9f20bfa6SDavid van Moolenbroek
2125*9f20bfa6SDavid van Moolenbroek state = D6_STATE(ifp);
2126*9f20bfa6SDavid van Moolenbroek if (dhcp6_checkstatusok(ifp, m, NULL, len) == -1)
2127*9f20bfa6SDavid van Moolenbroek return -1;
2128*9f20bfa6SDavid van Moolenbroek
2129*9f20bfa6SDavid van Moolenbroek state->renew = state->rebind = state->expire = 0;
2130*9f20bfa6SDavid van Moolenbroek state->lowpl = ND6_INFINITE_LIFETIME;
2131*9f20bfa6SDavid van Moolenbroek if (!acquired) {
2132*9f20bfa6SDavid van Moolenbroek clock_gettime(CLOCK_MONOTONIC, &aq);
2133*9f20bfa6SDavid van Moolenbroek acquired = &aq;
2134*9f20bfa6SDavid van Moolenbroek }
2135*9f20bfa6SDavid van Moolenbroek nia = dhcp6_findia(ifp, m, len, sfrom, acquired);
2136*9f20bfa6SDavid van Moolenbroek if (nia == 0) {
2137*9f20bfa6SDavid van Moolenbroek logger(ifp->ctx, LOG_ERR,
2138*9f20bfa6SDavid van Moolenbroek "%s: no useable IA found in lease", ifp->name);
2139*9f20bfa6SDavid van Moolenbroek return -1;
2140*9f20bfa6SDavid van Moolenbroek }
2141*9f20bfa6SDavid van Moolenbroek return nia;
2142*9f20bfa6SDavid van Moolenbroek }
2143*9f20bfa6SDavid van Moolenbroek
2144*9f20bfa6SDavid van Moolenbroek static ssize_t
dhcp6_writelease(const struct interface * ifp)2145*9f20bfa6SDavid van Moolenbroek dhcp6_writelease(const struct interface *ifp)
2146*9f20bfa6SDavid van Moolenbroek {
2147*9f20bfa6SDavid van Moolenbroek const struct dhcp6_state *state;
2148*9f20bfa6SDavid van Moolenbroek int fd;
2149*9f20bfa6SDavid van Moolenbroek ssize_t bytes;
2150*9f20bfa6SDavid van Moolenbroek
2151*9f20bfa6SDavid van Moolenbroek state = D6_CSTATE(ifp);
2152*9f20bfa6SDavid van Moolenbroek logger(ifp->ctx, LOG_DEBUG,
2153*9f20bfa6SDavid van Moolenbroek "%s: writing lease `%s'", ifp->name, state->leasefile);
2154*9f20bfa6SDavid van Moolenbroek
2155*9f20bfa6SDavid van Moolenbroek fd = open(state->leasefile, O_WRONLY | O_CREAT | O_TRUNC, 0644);
2156*9f20bfa6SDavid van Moolenbroek if (fd == -1) {
2157*9f20bfa6SDavid van Moolenbroek logger(ifp->ctx, LOG_ERR, "%s: dhcp6_writelease: %m", ifp->name);
2158*9f20bfa6SDavid van Moolenbroek return -1;
2159*9f20bfa6SDavid van Moolenbroek }
2160*9f20bfa6SDavid van Moolenbroek bytes = write(fd, state->new, state->new_len);
2161*9f20bfa6SDavid van Moolenbroek close(fd);
2162*9f20bfa6SDavid van Moolenbroek return bytes;
2163*9f20bfa6SDavid van Moolenbroek }
2164*9f20bfa6SDavid van Moolenbroek
2165*9f20bfa6SDavid van Moolenbroek static int
dhcp6_readlease(struct interface * ifp,int validate)2166*9f20bfa6SDavid van Moolenbroek dhcp6_readlease(struct interface *ifp, int validate)
2167*9f20bfa6SDavid van Moolenbroek {
2168*9f20bfa6SDavid van Moolenbroek struct dhcp6_state *state;
2169*9f20bfa6SDavid van Moolenbroek struct stat st;
2170*9f20bfa6SDavid van Moolenbroek int fd;
2171*9f20bfa6SDavid van Moolenbroek ssize_t bytes;
2172*9f20bfa6SDavid van Moolenbroek const struct dhcp6_option *o;
2173*9f20bfa6SDavid van Moolenbroek struct timespec acquired;
2174*9f20bfa6SDavid van Moolenbroek time_t now;
2175*9f20bfa6SDavid van Moolenbroek int retval;
2176*9f20bfa6SDavid van Moolenbroek
2177*9f20bfa6SDavid van Moolenbroek state = D6_STATE(ifp);
2178*9f20bfa6SDavid van Moolenbroek if (stat(state->leasefile, &st) == -1)
2179*9f20bfa6SDavid van Moolenbroek return -1;
2180*9f20bfa6SDavid van Moolenbroek logger(ifp->ctx, LOG_DEBUG, "%s: reading lease `%s'",
2181*9f20bfa6SDavid van Moolenbroek ifp->name, state->leasefile);
2182*9f20bfa6SDavid van Moolenbroek if (st.st_size > UINT32_MAX) {
2183*9f20bfa6SDavid van Moolenbroek errno = E2BIG;
2184*9f20bfa6SDavid van Moolenbroek return -1;
2185*9f20bfa6SDavid van Moolenbroek }
2186*9f20bfa6SDavid van Moolenbroek if ((fd = open(state->leasefile, O_RDONLY)) == -1)
2187*9f20bfa6SDavid van Moolenbroek return -1;
2188*9f20bfa6SDavid van Moolenbroek if ((state->new = malloc((size_t)st.st_size)) == NULL)
2189*9f20bfa6SDavid van Moolenbroek return -1;
2190*9f20bfa6SDavid van Moolenbroek retval = -1;
2191*9f20bfa6SDavid van Moolenbroek state->new_len = (size_t)st.st_size;
2192*9f20bfa6SDavid van Moolenbroek bytes = read(fd, state->new, state->new_len);
2193*9f20bfa6SDavid van Moolenbroek close(fd);
2194*9f20bfa6SDavid van Moolenbroek if (bytes != (ssize_t)state->new_len)
2195*9f20bfa6SDavid van Moolenbroek goto ex;
2196*9f20bfa6SDavid van Moolenbroek
2197*9f20bfa6SDavid van Moolenbroek /* If not validating IA's and if they have expired,
2198*9f20bfa6SDavid van Moolenbroek * skip to the auth check. */
2199*9f20bfa6SDavid van Moolenbroek if (!validate) {
2200*9f20bfa6SDavid van Moolenbroek fd = 0;
2201*9f20bfa6SDavid van Moolenbroek goto auth;
2202*9f20bfa6SDavid van Moolenbroek }
2203*9f20bfa6SDavid van Moolenbroek
2204*9f20bfa6SDavid van Moolenbroek if ((now = time(NULL)) == -1)
2205*9f20bfa6SDavid van Moolenbroek goto ex;
2206*9f20bfa6SDavid van Moolenbroek
2207*9f20bfa6SDavid van Moolenbroek clock_gettime(CLOCK_MONOTONIC, &acquired);
2208*9f20bfa6SDavid van Moolenbroek acquired.tv_sec -= now - st.st_mtime;
2209*9f20bfa6SDavid van Moolenbroek
2210*9f20bfa6SDavid van Moolenbroek /* Check to see if the lease is still valid */
2211*9f20bfa6SDavid van Moolenbroek fd = dhcp6_validatelease(ifp, state->new, state->new_len, NULL,
2212*9f20bfa6SDavid van Moolenbroek &acquired);
2213*9f20bfa6SDavid van Moolenbroek if (fd == -1)
2214*9f20bfa6SDavid van Moolenbroek goto ex;
2215*9f20bfa6SDavid van Moolenbroek
2216*9f20bfa6SDavid van Moolenbroek if (!(ifp->ctx->options & DHCPCD_DUMPLEASE) &&
2217*9f20bfa6SDavid van Moolenbroek state->expire != ND6_INFINITE_LIFETIME)
2218*9f20bfa6SDavid van Moolenbroek {
2219*9f20bfa6SDavid van Moolenbroek if ((time_t)state->expire < now - st.st_mtime) {
2220*9f20bfa6SDavid van Moolenbroek logger(ifp->ctx,
2221*9f20bfa6SDavid van Moolenbroek LOG_DEBUG,"%s: discarding expired lease",
2222*9f20bfa6SDavid van Moolenbroek ifp->name);
2223*9f20bfa6SDavid van Moolenbroek retval = 0;
2224*9f20bfa6SDavid van Moolenbroek goto ex;
2225*9f20bfa6SDavid van Moolenbroek }
2226*9f20bfa6SDavid van Moolenbroek }
2227*9f20bfa6SDavid van Moolenbroek
2228*9f20bfa6SDavid van Moolenbroek auth:
2229*9f20bfa6SDavid van Moolenbroek
2230*9f20bfa6SDavid van Moolenbroek retval = 0;
2231*9f20bfa6SDavid van Moolenbroek /* Authenticate the message */
2232*9f20bfa6SDavid van Moolenbroek o = dhcp6_getmoption(D6_OPTION_AUTH, state->new, state->new_len);
2233*9f20bfa6SDavid van Moolenbroek if (o) {
2234*9f20bfa6SDavid van Moolenbroek if (dhcp_auth_validate(&state->auth, &ifp->options->auth,
2235*9f20bfa6SDavid van Moolenbroek (uint8_t *)state->new, state->new_len, 6, state->new->type,
2236*9f20bfa6SDavid van Moolenbroek D6_COPTION_DATA(o), ntohs(o->len)) == NULL)
2237*9f20bfa6SDavid van Moolenbroek {
2238*9f20bfa6SDavid van Moolenbroek logger(ifp->ctx, LOG_DEBUG,
2239*9f20bfa6SDavid van Moolenbroek "%s: dhcp_auth_validate: %m", ifp->name);
2240*9f20bfa6SDavid van Moolenbroek logger(ifp->ctx, LOG_ERR,
2241*9f20bfa6SDavid van Moolenbroek "%s: authentication failed", ifp->name);
2242*9f20bfa6SDavid van Moolenbroek goto ex;
2243*9f20bfa6SDavid van Moolenbroek }
2244*9f20bfa6SDavid van Moolenbroek if (state->auth.token)
2245*9f20bfa6SDavid van Moolenbroek logger(ifp->ctx, LOG_DEBUG,
2246*9f20bfa6SDavid van Moolenbroek "%s: validated using 0x%08" PRIu32,
2247*9f20bfa6SDavid van Moolenbroek ifp->name, state->auth.token->secretid);
2248*9f20bfa6SDavid van Moolenbroek else
2249*9f20bfa6SDavid van Moolenbroek logger(ifp->ctx, LOG_DEBUG,
2250*9f20bfa6SDavid van Moolenbroek "%s: accepted reconfigure key", ifp->name);
2251*9f20bfa6SDavid van Moolenbroek } else if ((ifp->options->auth.options & DHCPCD_AUTH_SENDREQUIRE) ==
2252*9f20bfa6SDavid van Moolenbroek DHCPCD_AUTH_SENDREQUIRE)
2253*9f20bfa6SDavid van Moolenbroek {
2254*9f20bfa6SDavid van Moolenbroek logger(ifp->ctx, LOG_ERR,
2255*9f20bfa6SDavid van Moolenbroek "%s: authentication now required", ifp->name);
2256*9f20bfa6SDavid van Moolenbroek goto ex;
2257*9f20bfa6SDavid van Moolenbroek }
2258*9f20bfa6SDavid van Moolenbroek
2259*9f20bfa6SDavid van Moolenbroek return fd;
2260*9f20bfa6SDavid van Moolenbroek
2261*9f20bfa6SDavid van Moolenbroek ex:
2262*9f20bfa6SDavid van Moolenbroek dhcp6_freedrop_addrs(ifp, 0, NULL);
2263*9f20bfa6SDavid van Moolenbroek free(state->new);
2264*9f20bfa6SDavid van Moolenbroek state->new = NULL;
2265*9f20bfa6SDavid van Moolenbroek state->new_len = 0;
2266*9f20bfa6SDavid van Moolenbroek if (!(ifp->ctx->options & DHCPCD_DUMPLEASE))
2267*9f20bfa6SDavid van Moolenbroek unlink(state->leasefile);
2268*9f20bfa6SDavid van Moolenbroek return retval;
2269*9f20bfa6SDavid van Moolenbroek }
2270*9f20bfa6SDavid van Moolenbroek
2271*9f20bfa6SDavid van Moolenbroek static void
dhcp6_startinit(struct interface * ifp)2272*9f20bfa6SDavid van Moolenbroek dhcp6_startinit(struct interface *ifp)
2273*9f20bfa6SDavid van Moolenbroek {
2274*9f20bfa6SDavid van Moolenbroek struct dhcp6_state *state;
2275*9f20bfa6SDavid van Moolenbroek int r;
2276*9f20bfa6SDavid van Moolenbroek uint8_t has_ta, has_non_ta;
2277*9f20bfa6SDavid van Moolenbroek size_t i;
2278*9f20bfa6SDavid van Moolenbroek
2279*9f20bfa6SDavid van Moolenbroek state = D6_STATE(ifp);
2280*9f20bfa6SDavid van Moolenbroek state->state = DH6S_INIT;
2281*9f20bfa6SDavid van Moolenbroek state->expire = ND6_INFINITE_LIFETIME;
2282*9f20bfa6SDavid van Moolenbroek state->lowpl = ND6_INFINITE_LIFETIME;
2283*9f20bfa6SDavid van Moolenbroek
2284*9f20bfa6SDavid van Moolenbroek dhcp6_addrequestedaddrs(ifp);
2285*9f20bfa6SDavid van Moolenbroek has_ta = has_non_ta = 0;
2286*9f20bfa6SDavid van Moolenbroek for (i = 0; i < ifp->options->ia_len; i++) {
2287*9f20bfa6SDavid van Moolenbroek switch (ifp->options->ia[i].ia_type) {
2288*9f20bfa6SDavid van Moolenbroek case D6_OPTION_IA_TA:
2289*9f20bfa6SDavid van Moolenbroek has_ta = 1;
2290*9f20bfa6SDavid van Moolenbroek break;
2291*9f20bfa6SDavid van Moolenbroek default:
2292*9f20bfa6SDavid van Moolenbroek has_non_ta = 1;
2293*9f20bfa6SDavid van Moolenbroek }
2294*9f20bfa6SDavid van Moolenbroek }
2295*9f20bfa6SDavid van Moolenbroek
2296*9f20bfa6SDavid van Moolenbroek if (!(ifp->ctx->options & DHCPCD_TEST) &&
2297*9f20bfa6SDavid van Moolenbroek !(has_ta && !has_non_ta) &&
2298*9f20bfa6SDavid van Moolenbroek ifp->options->reboot != 0)
2299*9f20bfa6SDavid van Moolenbroek {
2300*9f20bfa6SDavid van Moolenbroek r = dhcp6_readlease(ifp, 1);
2301*9f20bfa6SDavid van Moolenbroek if (r == -1) {
2302*9f20bfa6SDavid van Moolenbroek if (errno != ENOENT)
2303*9f20bfa6SDavid van Moolenbroek logger(ifp->ctx, LOG_ERR,
2304*9f20bfa6SDavid van Moolenbroek "%s: dhcp6_readlease: %s: %m",
2305*9f20bfa6SDavid van Moolenbroek ifp->name, state->leasefile);
2306*9f20bfa6SDavid van Moolenbroek } else if (r != 0) {
2307*9f20bfa6SDavid van Moolenbroek /* RFC 3633 section 12.1 */
2308*9f20bfa6SDavid van Moolenbroek if (dhcp6_hasprefixdelegation(ifp))
2309*9f20bfa6SDavid van Moolenbroek dhcp6_startrebind(ifp);
2310*9f20bfa6SDavid van Moolenbroek else
2311*9f20bfa6SDavid van Moolenbroek dhcp6_startconfirm(ifp);
2312*9f20bfa6SDavid van Moolenbroek return;
2313*9f20bfa6SDavid van Moolenbroek }
2314*9f20bfa6SDavid van Moolenbroek }
2315*9f20bfa6SDavid van Moolenbroek dhcp6_startdiscover(ifp);
2316*9f20bfa6SDavid van Moolenbroek }
2317*9f20bfa6SDavid van Moolenbroek
2318*9f20bfa6SDavid van Moolenbroek static struct ipv6_addr *
dhcp6_ifdelegateaddr(struct interface * ifp,struct ipv6_addr * prefix,const struct if_sla * sla,struct if_ia * ia,struct interface * ifs)2319*9f20bfa6SDavid van Moolenbroek dhcp6_ifdelegateaddr(struct interface *ifp, struct ipv6_addr *prefix,
2320*9f20bfa6SDavid van Moolenbroek const struct if_sla *sla, struct if_ia *ia, struct interface *ifs)
2321*9f20bfa6SDavid van Moolenbroek {
2322*9f20bfa6SDavid van Moolenbroek struct dhcp6_state *state;
2323*9f20bfa6SDavid van Moolenbroek struct in6_addr addr;
2324*9f20bfa6SDavid van Moolenbroek struct ipv6_addr *a, *ap, *apn;
2325*9f20bfa6SDavid van Moolenbroek char sabuf[INET6_ADDRSTRLEN];
2326*9f20bfa6SDavid van Moolenbroek const char *sa;
2327*9f20bfa6SDavid van Moolenbroek int pfxlen;
2328*9f20bfa6SDavid van Moolenbroek
2329*9f20bfa6SDavid van Moolenbroek /* RFC6603 Section 4.2 */
2330*9f20bfa6SDavid van Moolenbroek if (strcmp(ifp->name, ifs->name) == 0) {
2331*9f20bfa6SDavid van Moolenbroek if (prefix->prefix_exclude_len == 0) {
2332*9f20bfa6SDavid van Moolenbroek /* Don't spam the log automatically */
2333*9f20bfa6SDavid van Moolenbroek if (sla)
2334*9f20bfa6SDavid van Moolenbroek logger(ifp->ctx, LOG_WARNING,
2335*9f20bfa6SDavid van Moolenbroek "%s: DHCPv6 server does not support "
2336*9f20bfa6SDavid van Moolenbroek "OPTION_PD_EXCLUDE",
2337*9f20bfa6SDavid van Moolenbroek ifp->name);
2338*9f20bfa6SDavid van Moolenbroek return NULL;
2339*9f20bfa6SDavid van Moolenbroek }
2340*9f20bfa6SDavid van Moolenbroek pfxlen = prefix->prefix_exclude_len;
2341*9f20bfa6SDavid van Moolenbroek memcpy(&addr, &prefix->prefix_exclude, sizeof(addr));
2342*9f20bfa6SDavid van Moolenbroek } else if ((pfxlen = dhcp6_delegateaddr(&addr, ifp, prefix,
2343*9f20bfa6SDavid van Moolenbroek sla, ia)) == -1)
2344*9f20bfa6SDavid van Moolenbroek return NULL;
2345*9f20bfa6SDavid van Moolenbroek
2346*9f20bfa6SDavid van Moolenbroek
2347*9f20bfa6SDavid van Moolenbroek a = calloc(1, sizeof(*a));
2348*9f20bfa6SDavid van Moolenbroek if (a == NULL) {
2349*9f20bfa6SDavid van Moolenbroek logger(ifp->ctx, LOG_ERR, "%s: %m", __func__);
2350*9f20bfa6SDavid van Moolenbroek return NULL;
2351*9f20bfa6SDavid van Moolenbroek }
2352*9f20bfa6SDavid van Moolenbroek a->iface = ifp;
2353*9f20bfa6SDavid van Moolenbroek a->flags = IPV6_AF_NEW | IPV6_AF_ONLINK;
2354*9f20bfa6SDavid van Moolenbroek a->dadcallback = dhcp6_dadcallback;
2355*9f20bfa6SDavid van Moolenbroek a->delegating_iface = ifs;
2356*9f20bfa6SDavid van Moolenbroek memcpy(&a->iaid, &prefix->iaid, sizeof(a->iaid));
2357*9f20bfa6SDavid van Moolenbroek a->created = a->acquired = prefix->acquired;
2358*9f20bfa6SDavid van Moolenbroek a->prefix_pltime = prefix->prefix_pltime;
2359*9f20bfa6SDavid van Moolenbroek a->prefix_vltime = prefix->prefix_vltime;
2360*9f20bfa6SDavid van Moolenbroek a->prefix = addr;
2361*9f20bfa6SDavid van Moolenbroek a->prefix_len = (uint8_t)pfxlen;
2362*9f20bfa6SDavid van Moolenbroek
2363*9f20bfa6SDavid van Moolenbroek /* Wang a 1 at the end as the prefix could be >64
2364*9f20bfa6SDavid van Moolenbroek * making SLAAC impossible. */
2365*9f20bfa6SDavid van Moolenbroek a->addr = a->prefix;
2366*9f20bfa6SDavid van Moolenbroek a->addr.s6_addr[sizeof(a->addr.s6_addr) - 1] =
2367*9f20bfa6SDavid van Moolenbroek (uint8_t)(a->addr.s6_addr[sizeof(a->addr.s6_addr) - 1] + 1);
2368*9f20bfa6SDavid van Moolenbroek
2369*9f20bfa6SDavid van Moolenbroek state = D6_STATE(ifp);
2370*9f20bfa6SDavid van Moolenbroek /* Remove any exiting address */
2371*9f20bfa6SDavid van Moolenbroek TAILQ_FOREACH_SAFE(ap, &state->addrs, next, apn) {
2372*9f20bfa6SDavid van Moolenbroek if (IN6_ARE_ADDR_EQUAL(&ap->addr, &a->addr)) {
2373*9f20bfa6SDavid van Moolenbroek TAILQ_REMOVE(&state->addrs, ap, next);
2374*9f20bfa6SDavid van Moolenbroek /* Keep our flags */
2375*9f20bfa6SDavid van Moolenbroek a->flags |= ap->flags;
2376*9f20bfa6SDavid van Moolenbroek a->flags &= ~IPV6_AF_NEW;
2377*9f20bfa6SDavid van Moolenbroek a->created = ap->created;
2378*9f20bfa6SDavid van Moolenbroek ipv6_freeaddr(ap);
2379*9f20bfa6SDavid van Moolenbroek }
2380*9f20bfa6SDavid van Moolenbroek }
2381*9f20bfa6SDavid van Moolenbroek
2382*9f20bfa6SDavid van Moolenbroek sa = inet_ntop(AF_INET6, &a->addr, sabuf, sizeof(sabuf));
2383*9f20bfa6SDavid van Moolenbroek snprintf(a->saddr, sizeof(a->saddr), "%s/%d", sa, a->prefix_len);
2384*9f20bfa6SDavid van Moolenbroek TAILQ_INSERT_TAIL(&state->addrs, a, next);
2385*9f20bfa6SDavid van Moolenbroek return a;
2386*9f20bfa6SDavid van Moolenbroek }
2387*9f20bfa6SDavid van Moolenbroek
2388*9f20bfa6SDavid van Moolenbroek static void
dhcp6_script_try_run(struct interface * ifp,int delegated)2389*9f20bfa6SDavid van Moolenbroek dhcp6_script_try_run(struct interface *ifp, int delegated)
2390*9f20bfa6SDavid van Moolenbroek {
2391*9f20bfa6SDavid van Moolenbroek struct dhcp6_state *state;
2392*9f20bfa6SDavid van Moolenbroek struct ipv6_addr *ap;
2393*9f20bfa6SDavid van Moolenbroek int completed;
2394*9f20bfa6SDavid van Moolenbroek
2395*9f20bfa6SDavid van Moolenbroek state = D6_STATE(ifp);
2396*9f20bfa6SDavid van Moolenbroek completed = 1;
2397*9f20bfa6SDavid van Moolenbroek /* If all addresses have completed DAD run the script */
2398*9f20bfa6SDavid van Moolenbroek TAILQ_FOREACH(ap, &state->addrs, next) {
2399*9f20bfa6SDavid van Moolenbroek if (!(ap->flags & IPV6_AF_ADDED))
2400*9f20bfa6SDavid van Moolenbroek continue;
2401*9f20bfa6SDavid van Moolenbroek if (ap->flags & IPV6_AF_ONLINK) {
2402*9f20bfa6SDavid van Moolenbroek if (!(ap->flags & IPV6_AF_DADCOMPLETED) &&
2403*9f20bfa6SDavid van Moolenbroek ipv6_iffindaddr(ap->iface, &ap->addr))
2404*9f20bfa6SDavid van Moolenbroek ap->flags |= IPV6_AF_DADCOMPLETED;
2405*9f20bfa6SDavid van Moolenbroek if ((ap->flags & IPV6_AF_DADCOMPLETED) == 0 &&
2406*9f20bfa6SDavid van Moolenbroek ((delegated && ap->delegating_iface) ||
2407*9f20bfa6SDavid van Moolenbroek (!delegated && !ap->delegating_iface)))
2408*9f20bfa6SDavid van Moolenbroek {
2409*9f20bfa6SDavid van Moolenbroek completed = 0;
2410*9f20bfa6SDavid van Moolenbroek break;
2411*9f20bfa6SDavid van Moolenbroek }
2412*9f20bfa6SDavid van Moolenbroek }
2413*9f20bfa6SDavid van Moolenbroek }
2414*9f20bfa6SDavid van Moolenbroek if (completed) {
2415*9f20bfa6SDavid van Moolenbroek script_runreason(ifp, delegated ? "DELEGATED6" : state->reason);
2416*9f20bfa6SDavid van Moolenbroek if (!delegated)
2417*9f20bfa6SDavid van Moolenbroek dhcpcd_daemonise(ifp->ctx);
2418*9f20bfa6SDavid van Moolenbroek } else
2419*9f20bfa6SDavid van Moolenbroek logger(ifp->ctx, LOG_DEBUG,
2420*9f20bfa6SDavid van Moolenbroek "%s: waiting for DHCPv6 DAD to complete", ifp->name);
2421*9f20bfa6SDavid van Moolenbroek }
2422*9f20bfa6SDavid van Moolenbroek
2423*9f20bfa6SDavid van Moolenbroek static void
dhcp6_delegate_prefix(struct interface * ifp)2424*9f20bfa6SDavid van Moolenbroek dhcp6_delegate_prefix(struct interface *ifp)
2425*9f20bfa6SDavid van Moolenbroek {
2426*9f20bfa6SDavid van Moolenbroek struct if_options *ifo;
2427*9f20bfa6SDavid van Moolenbroek struct dhcp6_state *state, *ifd_state;
2428*9f20bfa6SDavid van Moolenbroek struct ipv6_addr *ap;
2429*9f20bfa6SDavid van Moolenbroek size_t i, j, k;
2430*9f20bfa6SDavid van Moolenbroek struct if_ia *ia;
2431*9f20bfa6SDavid van Moolenbroek struct if_sla *sla;
2432*9f20bfa6SDavid van Moolenbroek struct interface *ifd;
2433*9f20bfa6SDavid van Moolenbroek uint8_t carrier_warned, abrt;
2434*9f20bfa6SDavid van Moolenbroek
2435*9f20bfa6SDavid van Moolenbroek ifo = ifp->options;
2436*9f20bfa6SDavid van Moolenbroek state = D6_STATE(ifp);
2437*9f20bfa6SDavid van Moolenbroek
2438*9f20bfa6SDavid van Moolenbroek /* Try to load configured interfaces for delegation that do not exist */
2439*9f20bfa6SDavid van Moolenbroek for (i = 0; i < ifo->ia_len; i++) {
2440*9f20bfa6SDavid van Moolenbroek ia = &ifo->ia[i];
2441*9f20bfa6SDavid van Moolenbroek for (j = 0; j < ia->sla_len; j++) {
2442*9f20bfa6SDavid van Moolenbroek sla = &ia->sla[j];
2443*9f20bfa6SDavid van Moolenbroek for (k = 0; k < i; j++)
2444*9f20bfa6SDavid van Moolenbroek if (strcmp(sla->ifname, ia->sla[j].ifname) == 0)
2445*9f20bfa6SDavid van Moolenbroek break;
2446*9f20bfa6SDavid van Moolenbroek if (j >= i &&
2447*9f20bfa6SDavid van Moolenbroek if_find(ifp->ctx->ifaces, sla->ifname) == NULL)
2448*9f20bfa6SDavid van Moolenbroek {
2449*9f20bfa6SDavid van Moolenbroek logger(ifp->ctx, LOG_INFO,
2450*9f20bfa6SDavid van Moolenbroek "%s: loading for delegation", sla->ifname);
2451*9f20bfa6SDavid van Moolenbroek if (dhcpcd_handleinterface(ifp->ctx, 2,
2452*9f20bfa6SDavid van Moolenbroek sla->ifname) == -1)
2453*9f20bfa6SDavid van Moolenbroek logger(ifp->ctx, LOG_ERR,
2454*9f20bfa6SDavid van Moolenbroek "%s: interface does not exist"
2455*9f20bfa6SDavid van Moolenbroek " for delegation",
2456*9f20bfa6SDavid van Moolenbroek sla->ifname);
2457*9f20bfa6SDavid van Moolenbroek }
2458*9f20bfa6SDavid van Moolenbroek }
2459*9f20bfa6SDavid van Moolenbroek }
2460*9f20bfa6SDavid van Moolenbroek
2461*9f20bfa6SDavid van Moolenbroek TAILQ_FOREACH(ifd, ifp->ctx->ifaces, next) {
2462*9f20bfa6SDavid van Moolenbroek k = 0;
2463*9f20bfa6SDavid van Moolenbroek carrier_warned = abrt = 0;
2464*9f20bfa6SDavid van Moolenbroek TAILQ_FOREACH(ap, &state->addrs, next) {
2465*9f20bfa6SDavid van Moolenbroek if (!(ap->flags & IPV6_AF_DELEGATEDPFX))
2466*9f20bfa6SDavid van Moolenbroek continue;
2467*9f20bfa6SDavid van Moolenbroek if (ap->flags & IPV6_AF_NEW) {
2468*9f20bfa6SDavid van Moolenbroek ap->flags &= ~IPV6_AF_NEW;
2469*9f20bfa6SDavid van Moolenbroek logger(ifp->ctx, LOG_DEBUG,
2470*9f20bfa6SDavid van Moolenbroek "%s: delegated prefix %s",
2471*9f20bfa6SDavid van Moolenbroek ifp->name, ap->saddr);
2472*9f20bfa6SDavid van Moolenbroek }
2473*9f20bfa6SDavid van Moolenbroek for (i = 0; i < ifo->ia_len; i++) {
2474*9f20bfa6SDavid van Moolenbroek ia = &ifo->ia[i];
2475*9f20bfa6SDavid van Moolenbroek if (memcmp(ia->iaid, ap->iaid,
2476*9f20bfa6SDavid van Moolenbroek sizeof(ia->iaid)))
2477*9f20bfa6SDavid van Moolenbroek continue;
2478*9f20bfa6SDavid van Moolenbroek if (ia->sla_len == 0) {
2479*9f20bfa6SDavid van Moolenbroek /* no SLA configured, so lets
2480*9f20bfa6SDavid van Moolenbroek * automate it */
2481*9f20bfa6SDavid van Moolenbroek if (ifd->carrier != LINK_UP) {
2482*9f20bfa6SDavid van Moolenbroek logger(ifp->ctx, LOG_DEBUG,
2483*9f20bfa6SDavid van Moolenbroek "%s: has no carrier, cannot"
2484*9f20bfa6SDavid van Moolenbroek " delegate addresses",
2485*9f20bfa6SDavid van Moolenbroek ifd->name);
2486*9f20bfa6SDavid van Moolenbroek carrier_warned = 1;
2487*9f20bfa6SDavid van Moolenbroek break;
2488*9f20bfa6SDavid van Moolenbroek }
2489*9f20bfa6SDavid van Moolenbroek if (dhcp6_ifdelegateaddr(ifd, ap,
2490*9f20bfa6SDavid van Moolenbroek NULL, ia, ifp))
2491*9f20bfa6SDavid van Moolenbroek k++;
2492*9f20bfa6SDavid van Moolenbroek }
2493*9f20bfa6SDavid van Moolenbroek for (j = 0; j < ia->sla_len; j++) {
2494*9f20bfa6SDavid van Moolenbroek sla = &ia->sla[j];
2495*9f20bfa6SDavid van Moolenbroek if (sla->sla_set && sla->sla == 0)
2496*9f20bfa6SDavid van Moolenbroek ap->flags |=
2497*9f20bfa6SDavid van Moolenbroek IPV6_AF_DELEGATEDZERO;
2498*9f20bfa6SDavid van Moolenbroek if (strcmp(ifd->name, sla->ifname))
2499*9f20bfa6SDavid van Moolenbroek continue;
2500*9f20bfa6SDavid van Moolenbroek if (ifd->carrier != LINK_UP) {
2501*9f20bfa6SDavid van Moolenbroek logger(ifp->ctx, LOG_DEBUG,
2502*9f20bfa6SDavid van Moolenbroek "%s: has no carrier, cannot"
2503*9f20bfa6SDavid van Moolenbroek " delegate addresses",
2504*9f20bfa6SDavid van Moolenbroek ifd->name);
2505*9f20bfa6SDavid van Moolenbroek carrier_warned = 1;
2506*9f20bfa6SDavid van Moolenbroek break;
2507*9f20bfa6SDavid van Moolenbroek }
2508*9f20bfa6SDavid van Moolenbroek if (dhcp6_ifdelegateaddr(ifd, ap,
2509*9f20bfa6SDavid van Moolenbroek sla, ia, ifp))
2510*9f20bfa6SDavid van Moolenbroek k++;
2511*9f20bfa6SDavid van Moolenbroek }
2512*9f20bfa6SDavid van Moolenbroek if (carrier_warned ||abrt)
2513*9f20bfa6SDavid van Moolenbroek break;
2514*9f20bfa6SDavid van Moolenbroek }
2515*9f20bfa6SDavid van Moolenbroek if (carrier_warned || abrt)
2516*9f20bfa6SDavid van Moolenbroek break;
2517*9f20bfa6SDavid van Moolenbroek }
2518*9f20bfa6SDavid van Moolenbroek if (k && !carrier_warned) {
2519*9f20bfa6SDavid van Moolenbroek ifd_state = D6_STATE(ifd);
2520*9f20bfa6SDavid van Moolenbroek ipv6_addaddrs(&ifd_state->addrs);
2521*9f20bfa6SDavid van Moolenbroek if_initrt6(ifd);
2522*9f20bfa6SDavid van Moolenbroek dhcp6_script_try_run(ifd, 1);
2523*9f20bfa6SDavid van Moolenbroek }
2524*9f20bfa6SDavid van Moolenbroek }
2525*9f20bfa6SDavid van Moolenbroek }
2526*9f20bfa6SDavid van Moolenbroek
2527*9f20bfa6SDavid van Moolenbroek static void
dhcp6_find_delegates1(void * arg)2528*9f20bfa6SDavid van Moolenbroek dhcp6_find_delegates1(void *arg)
2529*9f20bfa6SDavid van Moolenbroek {
2530*9f20bfa6SDavid van Moolenbroek
2531*9f20bfa6SDavid van Moolenbroek dhcp6_find_delegates(arg);
2532*9f20bfa6SDavid van Moolenbroek }
2533*9f20bfa6SDavid van Moolenbroek
2534*9f20bfa6SDavid van Moolenbroek size_t
dhcp6_find_delegates(struct interface * ifp)2535*9f20bfa6SDavid van Moolenbroek dhcp6_find_delegates(struct interface *ifp)
2536*9f20bfa6SDavid van Moolenbroek {
2537*9f20bfa6SDavid van Moolenbroek struct if_options *ifo;
2538*9f20bfa6SDavid van Moolenbroek struct dhcp6_state *state;
2539*9f20bfa6SDavid van Moolenbroek struct ipv6_addr *ap;
2540*9f20bfa6SDavid van Moolenbroek size_t i, j, k;
2541*9f20bfa6SDavid van Moolenbroek struct if_ia *ia;
2542*9f20bfa6SDavid van Moolenbroek struct if_sla *sla;
2543*9f20bfa6SDavid van Moolenbroek struct interface *ifd;
2544*9f20bfa6SDavid van Moolenbroek
2545*9f20bfa6SDavid van Moolenbroek k = 0;
2546*9f20bfa6SDavid van Moolenbroek TAILQ_FOREACH(ifd, ifp->ctx->ifaces, next) {
2547*9f20bfa6SDavid van Moolenbroek ifo = ifd->options;
2548*9f20bfa6SDavid van Moolenbroek state = D6_STATE(ifd);
2549*9f20bfa6SDavid van Moolenbroek if (state == NULL || state->state != DH6S_BOUND)
2550*9f20bfa6SDavid van Moolenbroek continue;
2551*9f20bfa6SDavid van Moolenbroek TAILQ_FOREACH(ap, &state->addrs, next) {
2552*9f20bfa6SDavid van Moolenbroek if (!(ap->flags & IPV6_AF_DELEGATEDPFX))
2553*9f20bfa6SDavid van Moolenbroek continue;
2554*9f20bfa6SDavid van Moolenbroek for (i = 0; i < ifo->ia_len; i++) {
2555*9f20bfa6SDavid van Moolenbroek ia = &ifo->ia[i];
2556*9f20bfa6SDavid van Moolenbroek if (memcmp(ia->iaid, ap->iaid,
2557*9f20bfa6SDavid van Moolenbroek sizeof(ia->iaid)))
2558*9f20bfa6SDavid van Moolenbroek continue;
2559*9f20bfa6SDavid van Moolenbroek for (j = 0; j < ia->sla_len; j++) {
2560*9f20bfa6SDavid van Moolenbroek sla = &ia->sla[j];
2561*9f20bfa6SDavid van Moolenbroek if (strcmp(ifp->name, sla->ifname))
2562*9f20bfa6SDavid van Moolenbroek continue;
2563*9f20bfa6SDavid van Moolenbroek if (ipv6_linklocal(ifp) == NULL) {
2564*9f20bfa6SDavid van Moolenbroek logger(ifp->ctx, LOG_DEBUG,
2565*9f20bfa6SDavid van Moolenbroek "%s: delaying adding"
2566*9f20bfa6SDavid van Moolenbroek " delegated addresses for"
2567*9f20bfa6SDavid van Moolenbroek " LL address",
2568*9f20bfa6SDavid van Moolenbroek ifp->name);
2569*9f20bfa6SDavid van Moolenbroek ipv6_addlinklocalcallback(ifp,
2570*9f20bfa6SDavid van Moolenbroek dhcp6_find_delegates1, ifp);
2571*9f20bfa6SDavid van Moolenbroek return 1;
2572*9f20bfa6SDavid van Moolenbroek }
2573*9f20bfa6SDavid van Moolenbroek if (dhcp6_ifdelegateaddr(ifp, ap,
2574*9f20bfa6SDavid van Moolenbroek sla, ia, ifd))
2575*9f20bfa6SDavid van Moolenbroek k++;
2576*9f20bfa6SDavid van Moolenbroek }
2577*9f20bfa6SDavid van Moolenbroek }
2578*9f20bfa6SDavid van Moolenbroek }
2579*9f20bfa6SDavid van Moolenbroek }
2580*9f20bfa6SDavid van Moolenbroek
2581*9f20bfa6SDavid van Moolenbroek if (k) {
2582*9f20bfa6SDavid van Moolenbroek logger(ifp->ctx, LOG_INFO,
2583*9f20bfa6SDavid van Moolenbroek "%s: adding delegated prefixes", ifp->name);
2584*9f20bfa6SDavid van Moolenbroek state = D6_STATE(ifp);
2585*9f20bfa6SDavid van Moolenbroek state->state = DH6S_DELEGATED;
2586*9f20bfa6SDavid van Moolenbroek ipv6_addaddrs(&state->addrs);
2587*9f20bfa6SDavid van Moolenbroek if_initrt6(ifp);
2588*9f20bfa6SDavid van Moolenbroek ipv6_buildroutes(ifp->ctx);
2589*9f20bfa6SDavid van Moolenbroek dhcp6_script_try_run(ifp, 1);
2590*9f20bfa6SDavid van Moolenbroek }
2591*9f20bfa6SDavid van Moolenbroek return k;
2592*9f20bfa6SDavid van Moolenbroek }
2593*9f20bfa6SDavid van Moolenbroek
2594*9f20bfa6SDavid van Moolenbroek /* ARGSUSED */
2595*9f20bfa6SDavid van Moolenbroek static void
dhcp6_handledata(void * arg)2596*9f20bfa6SDavid van Moolenbroek dhcp6_handledata(void *arg)
2597*9f20bfa6SDavid van Moolenbroek {
2598*9f20bfa6SDavid van Moolenbroek struct dhcpcd_ctx *dctx;
2599*9f20bfa6SDavid van Moolenbroek struct ipv6_ctx *ctx;
2600*9f20bfa6SDavid van Moolenbroek size_t i, len;
2601*9f20bfa6SDavid van Moolenbroek ssize_t bytes;
2602*9f20bfa6SDavid van Moolenbroek struct cmsghdr *cm;
2603*9f20bfa6SDavid van Moolenbroek struct in6_pktinfo pkt;
2604*9f20bfa6SDavid van Moolenbroek struct interface *ifp;
2605*9f20bfa6SDavid van Moolenbroek const char *op;
2606*9f20bfa6SDavid van Moolenbroek struct dhcp6_message *r;
2607*9f20bfa6SDavid van Moolenbroek struct dhcp6_state *state;
2608*9f20bfa6SDavid van Moolenbroek const struct dhcp6_option *o, *auth;
2609*9f20bfa6SDavid van Moolenbroek const struct dhcp_opt *opt;
2610*9f20bfa6SDavid van Moolenbroek const struct if_options *ifo;
2611*9f20bfa6SDavid van Moolenbroek struct ipv6_addr *ap;
2612*9f20bfa6SDavid van Moolenbroek uint8_t has_new;
2613*9f20bfa6SDavid van Moolenbroek int error;
2614*9f20bfa6SDavid van Moolenbroek uint32_t u32;
2615*9f20bfa6SDavid van Moolenbroek
2616*9f20bfa6SDavid van Moolenbroek dctx = arg;
2617*9f20bfa6SDavid van Moolenbroek ctx = dctx->ipv6;
2618*9f20bfa6SDavid van Moolenbroek ctx->rcvhdr.msg_controllen = CMSG_SPACE(sizeof(struct in6_pktinfo));
2619*9f20bfa6SDavid van Moolenbroek bytes = recvmsg(ctx->dhcp_fd, &ctx->rcvhdr, 0);
2620*9f20bfa6SDavid van Moolenbroek if (bytes == -1) {
2621*9f20bfa6SDavid van Moolenbroek logger(dctx, LOG_ERR, "%s: recvmsg: %m", __func__);
2622*9f20bfa6SDavid van Moolenbroek close(ctx->dhcp_fd);
2623*9f20bfa6SDavid van Moolenbroek eloop_event_delete(dctx->eloop, ctx->dhcp_fd);
2624*9f20bfa6SDavid van Moolenbroek ctx->dhcp_fd = -1;
2625*9f20bfa6SDavid van Moolenbroek return;
2626*9f20bfa6SDavid van Moolenbroek }
2627*9f20bfa6SDavid van Moolenbroek len = (size_t)bytes;
2628*9f20bfa6SDavid van Moolenbroek ctx->sfrom = inet_ntop(AF_INET6, &ctx->from.sin6_addr,
2629*9f20bfa6SDavid van Moolenbroek ctx->ntopbuf, sizeof(ctx->ntopbuf));
2630*9f20bfa6SDavid van Moolenbroek if (len < sizeof(struct dhcp6_message)) {
2631*9f20bfa6SDavid van Moolenbroek logger(dctx, LOG_ERR,
2632*9f20bfa6SDavid van Moolenbroek "DHCPv6 packet too short from %s", ctx->sfrom);
2633*9f20bfa6SDavid van Moolenbroek return;
2634*9f20bfa6SDavid van Moolenbroek }
2635*9f20bfa6SDavid van Moolenbroek
2636*9f20bfa6SDavid van Moolenbroek pkt.ipi6_ifindex = 0;
2637*9f20bfa6SDavid van Moolenbroek for (cm = (struct cmsghdr *)CMSG_FIRSTHDR(&ctx->rcvhdr);
2638*9f20bfa6SDavid van Moolenbroek cm;
2639*9f20bfa6SDavid van Moolenbroek cm = (struct cmsghdr *)CMSG_NXTHDR(&ctx->rcvhdr, cm))
2640*9f20bfa6SDavid van Moolenbroek {
2641*9f20bfa6SDavid van Moolenbroek if (cm->cmsg_level != IPPROTO_IPV6)
2642*9f20bfa6SDavid van Moolenbroek continue;
2643*9f20bfa6SDavid van Moolenbroek switch(cm->cmsg_type) {
2644*9f20bfa6SDavid van Moolenbroek case IPV6_PKTINFO:
2645*9f20bfa6SDavid van Moolenbroek if (cm->cmsg_len == CMSG_LEN(sizeof(pkt)))
2646*9f20bfa6SDavid van Moolenbroek memcpy(&pkt, CMSG_DATA(cm), sizeof(pkt));
2647*9f20bfa6SDavid van Moolenbroek break;
2648*9f20bfa6SDavid van Moolenbroek }
2649*9f20bfa6SDavid van Moolenbroek }
2650*9f20bfa6SDavid van Moolenbroek if (pkt.ipi6_ifindex == 0) {
2651*9f20bfa6SDavid van Moolenbroek logger(dctx, LOG_ERR,
2652*9f20bfa6SDavid van Moolenbroek "DHCPv6 reply did not contain index from %s", ctx->sfrom);
2653*9f20bfa6SDavid van Moolenbroek return;
2654*9f20bfa6SDavid van Moolenbroek }
2655*9f20bfa6SDavid van Moolenbroek
2656*9f20bfa6SDavid van Moolenbroek TAILQ_FOREACH(ifp, dctx->ifaces, next) {
2657*9f20bfa6SDavid van Moolenbroek if (ifp->index == (unsigned int)pkt.ipi6_ifindex)
2658*9f20bfa6SDavid van Moolenbroek break;
2659*9f20bfa6SDavid van Moolenbroek }
2660*9f20bfa6SDavid van Moolenbroek if (ifp == NULL) {
2661*9f20bfa6SDavid van Moolenbroek logger(dctx, LOG_DEBUG,
2662*9f20bfa6SDavid van Moolenbroek "DHCPv6 reply for unexpected interface from %s",
2663*9f20bfa6SDavid van Moolenbroek ctx->sfrom);
2664*9f20bfa6SDavid van Moolenbroek return;
2665*9f20bfa6SDavid van Moolenbroek }
2666*9f20bfa6SDavid van Moolenbroek
2667*9f20bfa6SDavid van Moolenbroek state = D6_STATE(ifp);
2668*9f20bfa6SDavid van Moolenbroek if (state == NULL || state->send == NULL) {
2669*9f20bfa6SDavid van Moolenbroek logger(ifp->ctx, LOG_DEBUG,
2670*9f20bfa6SDavid van Moolenbroek "%s: DHCPv6 reply received but not running", ifp->name);
2671*9f20bfa6SDavid van Moolenbroek return;
2672*9f20bfa6SDavid van Moolenbroek }
2673*9f20bfa6SDavid van Moolenbroek
2674*9f20bfa6SDavid van Moolenbroek r = (struct dhcp6_message *)ctx->rcvhdr.msg_iov[0].iov_base;
2675*9f20bfa6SDavid van Moolenbroek /* We're already bound and this message is for another machine */
2676*9f20bfa6SDavid van Moolenbroek /* XXX DELEGATED? */
2677*9f20bfa6SDavid van Moolenbroek if (r->type != DHCP6_RECONFIGURE &&
2678*9f20bfa6SDavid van Moolenbroek (state->state == DH6S_BOUND || state->state == DH6S_INFORMED))
2679*9f20bfa6SDavid van Moolenbroek {
2680*9f20bfa6SDavid van Moolenbroek logger(ifp->ctx, LOG_DEBUG,
2681*9f20bfa6SDavid van Moolenbroek "%s: DHCPv6 reply received but already bound", ifp->name);
2682*9f20bfa6SDavid van Moolenbroek return;
2683*9f20bfa6SDavid van Moolenbroek }
2684*9f20bfa6SDavid van Moolenbroek
2685*9f20bfa6SDavid van Moolenbroek if (r->type != DHCP6_RECONFIGURE &&
2686*9f20bfa6SDavid van Moolenbroek (r->xid[0] != state->send->xid[0] ||
2687*9f20bfa6SDavid van Moolenbroek r->xid[1] != state->send->xid[1] ||
2688*9f20bfa6SDavid van Moolenbroek r->xid[2] != state->send->xid[2]))
2689*9f20bfa6SDavid van Moolenbroek {
2690*9f20bfa6SDavid van Moolenbroek logger(dctx, LOG_DEBUG,
2691*9f20bfa6SDavid van Moolenbroek "%s: wrong xid 0x%02x%02x%02x"
2692*9f20bfa6SDavid van Moolenbroek " (expecting 0x%02x%02x%02x) from %s",
2693*9f20bfa6SDavid van Moolenbroek ifp->name,
2694*9f20bfa6SDavid van Moolenbroek r->xid[0], r->xid[1], r->xid[2],
2695*9f20bfa6SDavid van Moolenbroek state->send->xid[0], state->send->xid[1],
2696*9f20bfa6SDavid van Moolenbroek state->send->xid[2],
2697*9f20bfa6SDavid van Moolenbroek ctx->sfrom);
2698*9f20bfa6SDavid van Moolenbroek return;
2699*9f20bfa6SDavid van Moolenbroek }
2700*9f20bfa6SDavid van Moolenbroek
2701*9f20bfa6SDavid van Moolenbroek if (dhcp6_getmoption(D6_OPTION_SERVERID, r, len) == NULL) {
2702*9f20bfa6SDavid van Moolenbroek logger(ifp->ctx, LOG_DEBUG, "%s: no DHCPv6 server ID from %s",
2703*9f20bfa6SDavid van Moolenbroek ifp->name, ctx->sfrom);
2704*9f20bfa6SDavid van Moolenbroek return;
2705*9f20bfa6SDavid van Moolenbroek }
2706*9f20bfa6SDavid van Moolenbroek
2707*9f20bfa6SDavid van Moolenbroek o = dhcp6_getmoption(D6_OPTION_CLIENTID, r, len);
2708*9f20bfa6SDavid van Moolenbroek if (o == NULL || ntohs(o->len) != dctx->duid_len ||
2709*9f20bfa6SDavid van Moolenbroek memcmp(D6_COPTION_DATA(o), dctx->duid, dctx->duid_len) != 0)
2710*9f20bfa6SDavid van Moolenbroek {
2711*9f20bfa6SDavid van Moolenbroek logger(ifp->ctx, LOG_DEBUG, "%s: incorrect client ID from %s",
2712*9f20bfa6SDavid van Moolenbroek ifp->name, ctx->sfrom);
2713*9f20bfa6SDavid van Moolenbroek return;
2714*9f20bfa6SDavid van Moolenbroek }
2715*9f20bfa6SDavid van Moolenbroek
2716*9f20bfa6SDavid van Moolenbroek ifo = ifp->options;
2717*9f20bfa6SDavid van Moolenbroek for (i = 0, opt = dctx->dhcp6_opts;
2718*9f20bfa6SDavid van Moolenbroek i < dctx->dhcp6_opts_len;
2719*9f20bfa6SDavid van Moolenbroek i++, opt++)
2720*9f20bfa6SDavid van Moolenbroek {
2721*9f20bfa6SDavid van Moolenbroek if (has_option_mask(ifo->requiremask6, opt->option) &&
2722*9f20bfa6SDavid van Moolenbroek dhcp6_getmoption((uint16_t)opt->option, r, len) == NULL)
2723*9f20bfa6SDavid van Moolenbroek {
2724*9f20bfa6SDavid van Moolenbroek logger(ifp->ctx, LOG_WARNING,
2725*9f20bfa6SDavid van Moolenbroek "%s: reject DHCPv6 (no option %s) from %s",
2726*9f20bfa6SDavid van Moolenbroek ifp->name, opt->var, ctx->sfrom);
2727*9f20bfa6SDavid van Moolenbroek return;
2728*9f20bfa6SDavid van Moolenbroek }
2729*9f20bfa6SDavid van Moolenbroek if (has_option_mask(ifo->rejectmask6, opt->option) &&
2730*9f20bfa6SDavid van Moolenbroek dhcp6_getmoption((uint16_t)opt->option, r, len))
2731*9f20bfa6SDavid van Moolenbroek {
2732*9f20bfa6SDavid van Moolenbroek logger(ifp->ctx, LOG_WARNING,
2733*9f20bfa6SDavid van Moolenbroek "%s: reject DHCPv6 (option %s) from %s",
2734*9f20bfa6SDavid van Moolenbroek ifp->name, opt->var, ctx->sfrom);
2735*9f20bfa6SDavid van Moolenbroek return;
2736*9f20bfa6SDavid van Moolenbroek }
2737*9f20bfa6SDavid van Moolenbroek }
2738*9f20bfa6SDavid van Moolenbroek
2739*9f20bfa6SDavid van Moolenbroek /* Authenticate the message */
2740*9f20bfa6SDavid van Moolenbroek auth = dhcp6_getmoption(D6_OPTION_AUTH, r, len);
2741*9f20bfa6SDavid van Moolenbroek if (auth) {
2742*9f20bfa6SDavid van Moolenbroek if (dhcp_auth_validate(&state->auth, &ifo->auth,
2743*9f20bfa6SDavid van Moolenbroek (uint8_t *)r, len, 6, r->type,
2744*9f20bfa6SDavid van Moolenbroek D6_COPTION_DATA(auth), ntohs(auth->len)) == NULL)
2745*9f20bfa6SDavid van Moolenbroek {
2746*9f20bfa6SDavid van Moolenbroek logger(ifp->ctx, LOG_DEBUG, "dhcp_auth_validate: %m");
2747*9f20bfa6SDavid van Moolenbroek logger(ifp->ctx, LOG_ERR,
2748*9f20bfa6SDavid van Moolenbroek "%s: authentication failed from %s",
2749*9f20bfa6SDavid van Moolenbroek ifp->name, ctx->sfrom);
2750*9f20bfa6SDavid van Moolenbroek return;
2751*9f20bfa6SDavid van Moolenbroek }
2752*9f20bfa6SDavid van Moolenbroek if (state->auth.token)
2753*9f20bfa6SDavid van Moolenbroek logger(ifp->ctx, LOG_DEBUG,
2754*9f20bfa6SDavid van Moolenbroek "%s: validated using 0x%08" PRIu32,
2755*9f20bfa6SDavid van Moolenbroek ifp->name, state->auth.token->secretid);
2756*9f20bfa6SDavid van Moolenbroek else
2757*9f20bfa6SDavid van Moolenbroek logger(ifp->ctx, LOG_DEBUG,
2758*9f20bfa6SDavid van Moolenbroek "%s: accepted reconfigure key", ifp->name);
2759*9f20bfa6SDavid van Moolenbroek } else if (ifo->auth.options & DHCPCD_AUTH_SEND) {
2760*9f20bfa6SDavid van Moolenbroek if (ifo->auth.options & DHCPCD_AUTH_REQUIRE) {
2761*9f20bfa6SDavid van Moolenbroek logger(ifp->ctx, LOG_ERR,
2762*9f20bfa6SDavid van Moolenbroek "%s: no authentication from %s",
2763*9f20bfa6SDavid van Moolenbroek ifp->name, ctx->sfrom);
2764*9f20bfa6SDavid van Moolenbroek return;
2765*9f20bfa6SDavid van Moolenbroek }
2766*9f20bfa6SDavid van Moolenbroek logger(ifp->ctx, LOG_WARNING,
2767*9f20bfa6SDavid van Moolenbroek "%s: no authentication from %s", ifp->name, ctx->sfrom);
2768*9f20bfa6SDavid van Moolenbroek }
2769*9f20bfa6SDavid van Moolenbroek
2770*9f20bfa6SDavid van Moolenbroek op = dhcp6_get_op(r->type);
2771*9f20bfa6SDavid van Moolenbroek switch(r->type) {
2772*9f20bfa6SDavid van Moolenbroek case DHCP6_REPLY:
2773*9f20bfa6SDavid van Moolenbroek switch(state->state) {
2774*9f20bfa6SDavid van Moolenbroek case DH6S_INFORM:
2775*9f20bfa6SDavid van Moolenbroek if (dhcp6_checkstatusok(ifp, r, NULL, len) == -1)
2776*9f20bfa6SDavid van Moolenbroek return;
2777*9f20bfa6SDavid van Moolenbroek /* RFC4242 */
2778*9f20bfa6SDavid van Moolenbroek o = dhcp6_getmoption(D6_OPTION_INFO_REFRESH_TIME,
2779*9f20bfa6SDavid van Moolenbroek r, len);
2780*9f20bfa6SDavid van Moolenbroek if (o == NULL || ntohs(o->len) != sizeof(u32))
2781*9f20bfa6SDavid van Moolenbroek state->renew = IRT_DEFAULT;
2782*9f20bfa6SDavid van Moolenbroek else {
2783*9f20bfa6SDavid van Moolenbroek memcpy(&u32, D6_COPTION_DATA(o), sizeof(u32));
2784*9f20bfa6SDavid van Moolenbroek state->renew = ntohl(u32);
2785*9f20bfa6SDavid van Moolenbroek if (state->renew < IRT_MINIMUM)
2786*9f20bfa6SDavid van Moolenbroek state->renew = IRT_MINIMUM;
2787*9f20bfa6SDavid van Moolenbroek }
2788*9f20bfa6SDavid van Moolenbroek break;
2789*9f20bfa6SDavid van Moolenbroek case DH6S_CONFIRM:
2790*9f20bfa6SDavid van Moolenbroek error = dhcp6_checkstatusok(ifp, r, NULL, len);
2791*9f20bfa6SDavid van Moolenbroek /* If we got an OK status the chances are that we
2792*9f20bfa6SDavid van Moolenbroek * didn't get the IA's returned, so preserve them
2793*9f20bfa6SDavid van Moolenbroek * from our saved response */
2794*9f20bfa6SDavid van Moolenbroek if (error == 1)
2795*9f20bfa6SDavid van Moolenbroek goto recv;
2796*9f20bfa6SDavid van Moolenbroek if (error == -1 ||
2797*9f20bfa6SDavid van Moolenbroek dhcp6_validatelease(ifp, r, len,
2798*9f20bfa6SDavid van Moolenbroek ctx->sfrom, NULL) == -1)
2799*9f20bfa6SDavid van Moolenbroek {
2800*9f20bfa6SDavid van Moolenbroek dhcp6_startdiscover(ifp);
2801*9f20bfa6SDavid van Moolenbroek return;
2802*9f20bfa6SDavid van Moolenbroek }
2803*9f20bfa6SDavid van Moolenbroek break;
2804*9f20bfa6SDavid van Moolenbroek case DH6S_DISCOVER:
2805*9f20bfa6SDavid van Moolenbroek if (has_option_mask(ifo->requestmask6,
2806*9f20bfa6SDavid van Moolenbroek D6_OPTION_RAPID_COMMIT) &&
2807*9f20bfa6SDavid van Moolenbroek dhcp6_getmoption(D6_OPTION_RAPID_COMMIT, r, len))
2808*9f20bfa6SDavid van Moolenbroek state->state = DH6S_REQUEST;
2809*9f20bfa6SDavid van Moolenbroek else
2810*9f20bfa6SDavid van Moolenbroek op = NULL;
2811*9f20bfa6SDavid van Moolenbroek case DH6S_REQUEST: /* FALLTHROUGH */
2812*9f20bfa6SDavid van Moolenbroek case DH6S_RENEW: /* FALLTHROUGH */
2813*9f20bfa6SDavid van Moolenbroek case DH6S_REBIND:
2814*9f20bfa6SDavid van Moolenbroek if (dhcp6_validatelease(ifp, r, len,
2815*9f20bfa6SDavid van Moolenbroek ctx->sfrom, NULL) == -1)
2816*9f20bfa6SDavid van Moolenbroek {
2817*9f20bfa6SDavid van Moolenbroek /* PD doesn't use CONFIRM, so REBIND could
2818*9f20bfa6SDavid van Moolenbroek * throw up an invalid prefix if we
2819*9f20bfa6SDavid van Moolenbroek * changed link */
2820*9f20bfa6SDavid van Moolenbroek if (dhcp6_hasprefixdelegation(ifp))
2821*9f20bfa6SDavid van Moolenbroek dhcp6_startdiscover(ifp);
2822*9f20bfa6SDavid van Moolenbroek return;
2823*9f20bfa6SDavid van Moolenbroek }
2824*9f20bfa6SDavid van Moolenbroek break;
2825*9f20bfa6SDavid van Moolenbroek default:
2826*9f20bfa6SDavid van Moolenbroek op = NULL;
2827*9f20bfa6SDavid van Moolenbroek }
2828*9f20bfa6SDavid van Moolenbroek break;
2829*9f20bfa6SDavid van Moolenbroek case DHCP6_ADVERTISE:
2830*9f20bfa6SDavid van Moolenbroek if (state->state != DH6S_DISCOVER) {
2831*9f20bfa6SDavid van Moolenbroek op = NULL;
2832*9f20bfa6SDavid van Moolenbroek break;
2833*9f20bfa6SDavid van Moolenbroek }
2834*9f20bfa6SDavid van Moolenbroek /* RFC7083 */
2835*9f20bfa6SDavid van Moolenbroek o = dhcp6_getmoption(D6_OPTION_SOL_MAX_RT, r, len);
2836*9f20bfa6SDavid van Moolenbroek if (o && ntohs(o->len) >= sizeof(u32)) {
2837*9f20bfa6SDavid van Moolenbroek memcpy(&u32, D6_COPTION_DATA(o), sizeof(u32));
2838*9f20bfa6SDavid van Moolenbroek u32 = ntohl(u32);
2839*9f20bfa6SDavid van Moolenbroek if (u32 >= 60 && u32 <= 86400) {
2840*9f20bfa6SDavid van Moolenbroek logger(ifp->ctx, LOG_DEBUG,
2841*9f20bfa6SDavid van Moolenbroek "%s: SOL_MAX_RT %llu -> %d", ifp->name,
2842*9f20bfa6SDavid van Moolenbroek (unsigned long long)state->sol_max_rt, u32);
2843*9f20bfa6SDavid van Moolenbroek state->sol_max_rt = (time_t)u32;
2844*9f20bfa6SDavid van Moolenbroek } else
2845*9f20bfa6SDavid van Moolenbroek logger(ifp->ctx, LOG_ERR,
2846*9f20bfa6SDavid van Moolenbroek "%s: invalid SOL_MAX_RT %d",
2847*9f20bfa6SDavid van Moolenbroek ifp->name, u32);
2848*9f20bfa6SDavid van Moolenbroek }
2849*9f20bfa6SDavid van Moolenbroek o = dhcp6_getmoption(D6_OPTION_INF_MAX_RT, r, len);
2850*9f20bfa6SDavid van Moolenbroek if (o && ntohs(o->len) >= sizeof(u32)) {
2851*9f20bfa6SDavid van Moolenbroek memcpy(&u32, D6_COPTION_DATA(o), sizeof(u32));
2852*9f20bfa6SDavid van Moolenbroek u32 = ntohl(u32);
2853*9f20bfa6SDavid van Moolenbroek if (u32 >= 60 && u32 <= 86400) {
2854*9f20bfa6SDavid van Moolenbroek logger(ifp->ctx, LOG_DEBUG,
2855*9f20bfa6SDavid van Moolenbroek "%s: INF_MAX_RT %llu -> %d",
2856*9f20bfa6SDavid van Moolenbroek ifp->name,
2857*9f20bfa6SDavid van Moolenbroek (unsigned long long)state->inf_max_rt, u32);
2858*9f20bfa6SDavid van Moolenbroek state->inf_max_rt = (time_t)u32;
2859*9f20bfa6SDavid van Moolenbroek } else
2860*9f20bfa6SDavid van Moolenbroek logger(ifp->ctx, LOG_ERR,
2861*9f20bfa6SDavid van Moolenbroek "%s: invalid INF_MAX_RT %d",
2862*9f20bfa6SDavid van Moolenbroek ifp->name, u32);
2863*9f20bfa6SDavid van Moolenbroek }
2864*9f20bfa6SDavid van Moolenbroek if (dhcp6_validatelease(ifp, r, len, ctx->sfrom, NULL) == -1)
2865*9f20bfa6SDavid van Moolenbroek return;
2866*9f20bfa6SDavid van Moolenbroek break;
2867*9f20bfa6SDavid van Moolenbroek case DHCP6_RECONFIGURE:
2868*9f20bfa6SDavid van Moolenbroek if (auth == NULL) {
2869*9f20bfa6SDavid van Moolenbroek logger(ifp->ctx, LOG_ERR,
2870*9f20bfa6SDavid van Moolenbroek "%s: unauthenticated %s from %s",
2871*9f20bfa6SDavid van Moolenbroek ifp->name, op, ctx->sfrom);
2872*9f20bfa6SDavid van Moolenbroek if (ifo->auth.options & DHCPCD_AUTH_REQUIRE)
2873*9f20bfa6SDavid van Moolenbroek return;
2874*9f20bfa6SDavid van Moolenbroek }
2875*9f20bfa6SDavid van Moolenbroek logger(ifp->ctx, LOG_INFO, "%s: %s from %s",
2876*9f20bfa6SDavid van Moolenbroek ifp->name, op, ctx->sfrom);
2877*9f20bfa6SDavid van Moolenbroek o = dhcp6_getmoption(D6_OPTION_RECONF_MSG, r, len);
2878*9f20bfa6SDavid van Moolenbroek if (o == NULL) {
2879*9f20bfa6SDavid van Moolenbroek logger(ifp->ctx, LOG_ERR,
2880*9f20bfa6SDavid van Moolenbroek "%s: missing Reconfigure Message option",
2881*9f20bfa6SDavid van Moolenbroek ifp->name);
2882*9f20bfa6SDavid van Moolenbroek return;
2883*9f20bfa6SDavid van Moolenbroek }
2884*9f20bfa6SDavid van Moolenbroek if (ntohs(o->len) != 1) {
2885*9f20bfa6SDavid van Moolenbroek logger(ifp->ctx, LOG_ERR,
2886*9f20bfa6SDavid van Moolenbroek "%s: missing Reconfigure Message type", ifp->name);
2887*9f20bfa6SDavid van Moolenbroek return;
2888*9f20bfa6SDavid van Moolenbroek }
2889*9f20bfa6SDavid van Moolenbroek switch(*D6_COPTION_DATA(o)) {
2890*9f20bfa6SDavid van Moolenbroek case DHCP6_RENEW:
2891*9f20bfa6SDavid van Moolenbroek if (state->state != DH6S_BOUND) {
2892*9f20bfa6SDavid van Moolenbroek logger(ifp->ctx, LOG_ERR,
2893*9f20bfa6SDavid van Moolenbroek "%s: not bound, ignoring %s",
2894*9f20bfa6SDavid van Moolenbroek ifp->name, op);
2895*9f20bfa6SDavid van Moolenbroek return;
2896*9f20bfa6SDavid van Moolenbroek }
2897*9f20bfa6SDavid van Moolenbroek eloop_timeout_delete(ifp->ctx->eloop,
2898*9f20bfa6SDavid van Moolenbroek dhcp6_startrenew, ifp);
2899*9f20bfa6SDavid van Moolenbroek dhcp6_startrenew(ifp);
2900*9f20bfa6SDavid van Moolenbroek break;
2901*9f20bfa6SDavid van Moolenbroek case DHCP6_INFORMATION_REQ:
2902*9f20bfa6SDavid van Moolenbroek if (state->state != DH6S_INFORMED) {
2903*9f20bfa6SDavid van Moolenbroek logger(ifp->ctx, LOG_ERR,
2904*9f20bfa6SDavid van Moolenbroek "%s: not informed, ignoring %s",
2905*9f20bfa6SDavid van Moolenbroek ifp->name, op);
2906*9f20bfa6SDavid van Moolenbroek return;
2907*9f20bfa6SDavid van Moolenbroek }
2908*9f20bfa6SDavid van Moolenbroek eloop_timeout_delete(ifp->ctx->eloop,
2909*9f20bfa6SDavid van Moolenbroek dhcp6_sendinform, ifp);
2910*9f20bfa6SDavid van Moolenbroek dhcp6_startinform(ifp);
2911*9f20bfa6SDavid van Moolenbroek break;
2912*9f20bfa6SDavid van Moolenbroek default:
2913*9f20bfa6SDavid van Moolenbroek logger(ifp->ctx, LOG_ERR,
2914*9f20bfa6SDavid van Moolenbroek "%s: unsupported %s type %d",
2915*9f20bfa6SDavid van Moolenbroek ifp->name, op, *D6_COPTION_DATA(o));
2916*9f20bfa6SDavid van Moolenbroek break;
2917*9f20bfa6SDavid van Moolenbroek }
2918*9f20bfa6SDavid van Moolenbroek return;
2919*9f20bfa6SDavid van Moolenbroek default:
2920*9f20bfa6SDavid van Moolenbroek logger(ifp->ctx, LOG_ERR, "%s: invalid DHCP6 type %s (%d)",
2921*9f20bfa6SDavid van Moolenbroek ifp->name, op, r->type);
2922*9f20bfa6SDavid van Moolenbroek return;
2923*9f20bfa6SDavid van Moolenbroek }
2924*9f20bfa6SDavid van Moolenbroek if (op == NULL) {
2925*9f20bfa6SDavid van Moolenbroek logger(ifp->ctx, LOG_WARNING,
2926*9f20bfa6SDavid van Moolenbroek "%s: invalid state for DHCP6 type %s (%d)",
2927*9f20bfa6SDavid van Moolenbroek ifp->name, op, r->type);
2928*9f20bfa6SDavid van Moolenbroek return;
2929*9f20bfa6SDavid van Moolenbroek }
2930*9f20bfa6SDavid van Moolenbroek
2931*9f20bfa6SDavid van Moolenbroek if (state->recv_len < (size_t)len) {
2932*9f20bfa6SDavid van Moolenbroek free(state->recv);
2933*9f20bfa6SDavid van Moolenbroek state->recv = malloc(len);
2934*9f20bfa6SDavid van Moolenbroek if (state->recv == NULL) {
2935*9f20bfa6SDavid van Moolenbroek logger(ifp->ctx, LOG_ERR,
2936*9f20bfa6SDavid van Moolenbroek "%s: malloc recv: %m", ifp->name);
2937*9f20bfa6SDavid van Moolenbroek return;
2938*9f20bfa6SDavid van Moolenbroek }
2939*9f20bfa6SDavid van Moolenbroek }
2940*9f20bfa6SDavid van Moolenbroek memcpy(state->recv, r, len);
2941*9f20bfa6SDavid van Moolenbroek state->recv_len = len;
2942*9f20bfa6SDavid van Moolenbroek
2943*9f20bfa6SDavid van Moolenbroek switch(r->type) {
2944*9f20bfa6SDavid van Moolenbroek case DHCP6_ADVERTISE:
2945*9f20bfa6SDavid van Moolenbroek if (state->state == DH6S_REQUEST) /* rapid commit */
2946*9f20bfa6SDavid van Moolenbroek break;
2947*9f20bfa6SDavid van Moolenbroek ap = TAILQ_FIRST(&state->addrs);
2948*9f20bfa6SDavid van Moolenbroek logger(ifp->ctx, LOG_INFO, "%s: ADV %s from %s",
2949*9f20bfa6SDavid van Moolenbroek ifp->name, ap->saddr, ctx->sfrom);
2950*9f20bfa6SDavid van Moolenbroek if (ifp->ctx->options & DHCPCD_TEST)
2951*9f20bfa6SDavid van Moolenbroek break;
2952*9f20bfa6SDavid van Moolenbroek dhcp6_startrequest(ifp);
2953*9f20bfa6SDavid van Moolenbroek return;
2954*9f20bfa6SDavid van Moolenbroek }
2955*9f20bfa6SDavid van Moolenbroek
2956*9f20bfa6SDavid van Moolenbroek recv:
2957*9f20bfa6SDavid van Moolenbroek has_new = 0;
2958*9f20bfa6SDavid van Moolenbroek TAILQ_FOREACH(ap, &state->addrs, next) {
2959*9f20bfa6SDavid van Moolenbroek if (ap->flags & IPV6_AF_NEW) {
2960*9f20bfa6SDavid van Moolenbroek has_new = 1;
2961*9f20bfa6SDavid van Moolenbroek break;
2962*9f20bfa6SDavid van Moolenbroek }
2963*9f20bfa6SDavid van Moolenbroek }
2964*9f20bfa6SDavid van Moolenbroek logger(ifp->ctx, has_new ? LOG_INFO : LOG_DEBUG,
2965*9f20bfa6SDavid van Moolenbroek "%s: %s received from %s", ifp->name, op, ctx->sfrom);
2966*9f20bfa6SDavid van Moolenbroek
2967*9f20bfa6SDavid van Moolenbroek state->reason = NULL;
2968*9f20bfa6SDavid van Moolenbroek eloop_timeout_delete(ifp->ctx->eloop, NULL, ifp);
2969*9f20bfa6SDavid van Moolenbroek switch(state->state) {
2970*9f20bfa6SDavid van Moolenbroek case DH6S_INFORM:
2971*9f20bfa6SDavid van Moolenbroek state->rebind = 0;
2972*9f20bfa6SDavid van Moolenbroek state->expire = ND6_INFINITE_LIFETIME;
2973*9f20bfa6SDavid van Moolenbroek state->lowpl = ND6_INFINITE_LIFETIME;
2974*9f20bfa6SDavid van Moolenbroek state->reason = "INFORM6";
2975*9f20bfa6SDavid van Moolenbroek break;
2976*9f20bfa6SDavid van Moolenbroek case DH6S_REQUEST:
2977*9f20bfa6SDavid van Moolenbroek if (state->reason == NULL)
2978*9f20bfa6SDavid van Moolenbroek state->reason = "BOUND6";
2979*9f20bfa6SDavid van Moolenbroek /* FALLTHROUGH */
2980*9f20bfa6SDavid van Moolenbroek case DH6S_RENEW:
2981*9f20bfa6SDavid van Moolenbroek if (state->reason == NULL)
2982*9f20bfa6SDavid van Moolenbroek state->reason = "RENEW6";
2983*9f20bfa6SDavid van Moolenbroek /* FALLTHROUGH */
2984*9f20bfa6SDavid van Moolenbroek case DH6S_REBIND:
2985*9f20bfa6SDavid van Moolenbroek if (state->reason == NULL)
2986*9f20bfa6SDavid van Moolenbroek state->reason = "REBIND6";
2987*9f20bfa6SDavid van Moolenbroek /* FALLTHROUGH */
2988*9f20bfa6SDavid van Moolenbroek case DH6S_CONFIRM:
2989*9f20bfa6SDavid van Moolenbroek if (state->reason == NULL)
2990*9f20bfa6SDavid van Moolenbroek state->reason = "REBOOT6";
2991*9f20bfa6SDavid van Moolenbroek if (state->renew == 0) {
2992*9f20bfa6SDavid van Moolenbroek if (state->expire == ND6_INFINITE_LIFETIME)
2993*9f20bfa6SDavid van Moolenbroek state->renew = ND6_INFINITE_LIFETIME;
2994*9f20bfa6SDavid van Moolenbroek else if (state->lowpl != ND6_INFINITE_LIFETIME)
2995*9f20bfa6SDavid van Moolenbroek state->renew = (uint32_t)(state->lowpl * 0.5);
2996*9f20bfa6SDavid van Moolenbroek }
2997*9f20bfa6SDavid van Moolenbroek if (state->rebind == 0) {
2998*9f20bfa6SDavid van Moolenbroek if (state->expire == ND6_INFINITE_LIFETIME)
2999*9f20bfa6SDavid van Moolenbroek state->rebind = ND6_INFINITE_LIFETIME;
3000*9f20bfa6SDavid van Moolenbroek else if (state->lowpl != ND6_INFINITE_LIFETIME)
3001*9f20bfa6SDavid van Moolenbroek state->rebind = (uint32_t)(state->lowpl * 0.8);
3002*9f20bfa6SDavid van Moolenbroek }
3003*9f20bfa6SDavid van Moolenbroek break;
3004*9f20bfa6SDavid van Moolenbroek default:
3005*9f20bfa6SDavid van Moolenbroek state->reason = "UNKNOWN6";
3006*9f20bfa6SDavid van Moolenbroek break;
3007*9f20bfa6SDavid van Moolenbroek }
3008*9f20bfa6SDavid van Moolenbroek
3009*9f20bfa6SDavid van Moolenbroek if (state->state != DH6S_CONFIRM) {
3010*9f20bfa6SDavid van Moolenbroek free(state->old);
3011*9f20bfa6SDavid van Moolenbroek state->old = state->new;
3012*9f20bfa6SDavid van Moolenbroek state->old_len = state->new_len;
3013*9f20bfa6SDavid van Moolenbroek state->new = state->recv;
3014*9f20bfa6SDavid van Moolenbroek state->new_len = state->recv_len;
3015*9f20bfa6SDavid van Moolenbroek state->recv = NULL;
3016*9f20bfa6SDavid van Moolenbroek state->recv_len = 0;
3017*9f20bfa6SDavid van Moolenbroek }
3018*9f20bfa6SDavid van Moolenbroek
3019*9f20bfa6SDavid van Moolenbroek if (ifp->ctx->options & DHCPCD_TEST)
3020*9f20bfa6SDavid van Moolenbroek script_runreason(ifp, "TEST");
3021*9f20bfa6SDavid van Moolenbroek else {
3022*9f20bfa6SDavid van Moolenbroek if (state->state == DH6S_INFORM)
3023*9f20bfa6SDavid van Moolenbroek state->state = DH6S_INFORMED;
3024*9f20bfa6SDavid van Moolenbroek else
3025*9f20bfa6SDavid van Moolenbroek state->state = DH6S_BOUND;
3026*9f20bfa6SDavid van Moolenbroek if (state->renew && state->renew != ND6_INFINITE_LIFETIME)
3027*9f20bfa6SDavid van Moolenbroek eloop_timeout_add_sec(ifp->ctx->eloop,
3028*9f20bfa6SDavid van Moolenbroek (time_t)state->renew,
3029*9f20bfa6SDavid van Moolenbroek state->state == DH6S_INFORMED ?
3030*9f20bfa6SDavid van Moolenbroek dhcp6_startinform : dhcp6_startrenew, ifp);
3031*9f20bfa6SDavid van Moolenbroek if (state->rebind && state->rebind != ND6_INFINITE_LIFETIME)
3032*9f20bfa6SDavid van Moolenbroek eloop_timeout_add_sec(ifp->ctx->eloop,
3033*9f20bfa6SDavid van Moolenbroek (time_t)state->rebind, dhcp6_startrebind, ifp);
3034*9f20bfa6SDavid van Moolenbroek if (state->expire != ND6_INFINITE_LIFETIME)
3035*9f20bfa6SDavid van Moolenbroek eloop_timeout_add_sec(ifp->ctx->eloop,
3036*9f20bfa6SDavid van Moolenbroek (time_t)state->expire, dhcp6_startexpire, ifp);
3037*9f20bfa6SDavid van Moolenbroek
3038*9f20bfa6SDavid van Moolenbroek ipv6nd_runignoredra(ifp);
3039*9f20bfa6SDavid van Moolenbroek ipv6_addaddrs(&state->addrs);
3040*9f20bfa6SDavid van Moolenbroek dhcp6_delegate_prefix(ifp);
3041*9f20bfa6SDavid van Moolenbroek
3042*9f20bfa6SDavid van Moolenbroek if (state->state == DH6S_INFORMED)
3043*9f20bfa6SDavid van Moolenbroek logger(ifp->ctx, has_new ? LOG_INFO : LOG_DEBUG,
3044*9f20bfa6SDavid van Moolenbroek "%s: refresh in %"PRIu32" seconds",
3045*9f20bfa6SDavid van Moolenbroek ifp->name, state->renew);
3046*9f20bfa6SDavid van Moolenbroek else if (state->renew || state->rebind)
3047*9f20bfa6SDavid van Moolenbroek logger(ifp->ctx, has_new ? LOG_INFO : LOG_DEBUG,
3048*9f20bfa6SDavid van Moolenbroek "%s: renew in %"PRIu32" seconds,"
3049*9f20bfa6SDavid van Moolenbroek " rebind in %"PRIu32" seconds",
3050*9f20bfa6SDavid van Moolenbroek ifp->name, state->renew, state->rebind);
3051*9f20bfa6SDavid van Moolenbroek else if (state->expire == 0)
3052*9f20bfa6SDavid van Moolenbroek logger(ifp->ctx, has_new ? LOG_INFO : LOG_DEBUG,
3053*9f20bfa6SDavid van Moolenbroek "%s: will expire", ifp->name);
3054*9f20bfa6SDavid van Moolenbroek if_initrt6(ifp);
3055*9f20bfa6SDavid van Moolenbroek ipv6_buildroutes(ifp->ctx);
3056*9f20bfa6SDavid van Moolenbroek dhcp6_writelease(ifp);
3057*9f20bfa6SDavid van Moolenbroek dhcp6_script_try_run(ifp, 0);
3058*9f20bfa6SDavid van Moolenbroek }
3059*9f20bfa6SDavid van Moolenbroek
3060*9f20bfa6SDavid van Moolenbroek if (ifp->ctx->options & DHCPCD_TEST ||
3061*9f20bfa6SDavid van Moolenbroek (ifp->options->options & DHCPCD_INFORM &&
3062*9f20bfa6SDavid van Moolenbroek !(ifp->ctx->options & DHCPCD_MASTER)))
3063*9f20bfa6SDavid van Moolenbroek {
3064*9f20bfa6SDavid van Moolenbroek eloop_exit(ifp->ctx->eloop, EXIT_SUCCESS);
3065*9f20bfa6SDavid van Moolenbroek }
3066*9f20bfa6SDavid van Moolenbroek }
3067*9f20bfa6SDavid van Moolenbroek
3068*9f20bfa6SDavid van Moolenbroek static int
dhcp6_open(struct dhcpcd_ctx * dctx)3069*9f20bfa6SDavid van Moolenbroek dhcp6_open(struct dhcpcd_ctx *dctx)
3070*9f20bfa6SDavid van Moolenbroek {
3071*9f20bfa6SDavid van Moolenbroek struct ipv6_ctx *ctx;
3072*9f20bfa6SDavid van Moolenbroek struct sockaddr_in6 sa;
3073*9f20bfa6SDavid van Moolenbroek int n;
3074*9f20bfa6SDavid van Moolenbroek
3075*9f20bfa6SDavid van Moolenbroek memset(&sa, 0, sizeof(sa));
3076*9f20bfa6SDavid van Moolenbroek sa.sin6_family = AF_INET6;
3077*9f20bfa6SDavid van Moolenbroek sa.sin6_port = htons(DHCP6_CLIENT_PORT);
3078*9f20bfa6SDavid van Moolenbroek #ifdef BSD
3079*9f20bfa6SDavid van Moolenbroek sa.sin6_len = sizeof(sa);
3080*9f20bfa6SDavid van Moolenbroek #endif
3081*9f20bfa6SDavid van Moolenbroek
3082*9f20bfa6SDavid van Moolenbroek ctx = dctx->ipv6;
3083*9f20bfa6SDavid van Moolenbroek ctx->dhcp_fd = xsocket(PF_INET6, SOCK_DGRAM, IPPROTO_UDP,
3084*9f20bfa6SDavid van Moolenbroek O_NONBLOCK|O_CLOEXEC);
3085*9f20bfa6SDavid van Moolenbroek if (ctx->dhcp_fd == -1)
3086*9f20bfa6SDavid van Moolenbroek return -1;
3087*9f20bfa6SDavid van Moolenbroek
3088*9f20bfa6SDavid van Moolenbroek n = 1;
3089*9f20bfa6SDavid van Moolenbroek if (setsockopt(ctx->dhcp_fd, SOL_SOCKET, SO_REUSEADDR,
3090*9f20bfa6SDavid van Moolenbroek &n, sizeof(n)) == -1)
3091*9f20bfa6SDavid van Moolenbroek goto errexit;
3092*9f20bfa6SDavid van Moolenbroek
3093*9f20bfa6SDavid van Moolenbroek n = 1;
3094*9f20bfa6SDavid van Moolenbroek if (setsockopt(ctx->dhcp_fd, SOL_SOCKET, SO_BROADCAST,
3095*9f20bfa6SDavid van Moolenbroek &n, sizeof(n)) == -1)
3096*9f20bfa6SDavid van Moolenbroek goto errexit;
3097*9f20bfa6SDavid van Moolenbroek
3098*9f20bfa6SDavid van Moolenbroek #ifdef SO_REUSEPORT
3099*9f20bfa6SDavid van Moolenbroek n = 1;
3100*9f20bfa6SDavid van Moolenbroek if (setsockopt(ctx->dhcp_fd, SOL_SOCKET, SO_REUSEPORT,
3101*9f20bfa6SDavid van Moolenbroek &n, sizeof(n)) == -1)
3102*9f20bfa6SDavid van Moolenbroek logger(dctx, LOG_WARNING, "setsockopt: SO_REUSEPORT: %m");
3103*9f20bfa6SDavid van Moolenbroek #endif
3104*9f20bfa6SDavid van Moolenbroek
3105*9f20bfa6SDavid van Moolenbroek if (bind(ctx->dhcp_fd, (struct sockaddr *)&sa, sizeof(sa)) == -1)
3106*9f20bfa6SDavid van Moolenbroek goto errexit;
3107*9f20bfa6SDavid van Moolenbroek
3108*9f20bfa6SDavid van Moolenbroek n = 1;
3109*9f20bfa6SDavid van Moolenbroek if (setsockopt(ctx->dhcp_fd, IPPROTO_IPV6, IPV6_RECVPKTINFO,
3110*9f20bfa6SDavid van Moolenbroek &n, sizeof(n)) == -1)
3111*9f20bfa6SDavid van Moolenbroek goto errexit;
3112*9f20bfa6SDavid van Moolenbroek
3113*9f20bfa6SDavid van Moolenbroek eloop_event_add(dctx->eloop, ctx->dhcp_fd,
3114*9f20bfa6SDavid van Moolenbroek dhcp6_handledata, dctx, NULL, NULL);
3115*9f20bfa6SDavid van Moolenbroek return 0;
3116*9f20bfa6SDavid van Moolenbroek
3117*9f20bfa6SDavid van Moolenbroek errexit:
3118*9f20bfa6SDavid van Moolenbroek close(ctx->dhcp_fd);
3119*9f20bfa6SDavid van Moolenbroek ctx->dhcp_fd = -1;
3120*9f20bfa6SDavid van Moolenbroek return -1;
3121*9f20bfa6SDavid van Moolenbroek }
3122*9f20bfa6SDavid van Moolenbroek
3123*9f20bfa6SDavid van Moolenbroek static void
dhcp6_start1(void * arg)3124*9f20bfa6SDavid van Moolenbroek dhcp6_start1(void *arg)
3125*9f20bfa6SDavid van Moolenbroek {
3126*9f20bfa6SDavid van Moolenbroek struct interface *ifp = arg;
3127*9f20bfa6SDavid van Moolenbroek struct if_options *ifo = ifp->options;
3128*9f20bfa6SDavid van Moolenbroek struct dhcp6_state *state;
3129*9f20bfa6SDavid van Moolenbroek size_t i;
3130*9f20bfa6SDavid van Moolenbroek const struct dhcp_compat *dhc;
3131*9f20bfa6SDavid van Moolenbroek
3132*9f20bfa6SDavid van Moolenbroek state = D6_STATE(ifp);
3133*9f20bfa6SDavid van Moolenbroek /* If no DHCPv6 options are configured,
3134*9f20bfa6SDavid van Moolenbroek match configured DHCPv4 options to DHCPv6 equivalents. */
3135*9f20bfa6SDavid van Moolenbroek for (i = 0; i < sizeof(ifo->requestmask6); i++) {
3136*9f20bfa6SDavid van Moolenbroek if (ifo->requestmask6[i] != '\0')
3137*9f20bfa6SDavid van Moolenbroek break;
3138*9f20bfa6SDavid van Moolenbroek }
3139*9f20bfa6SDavid van Moolenbroek if (i == sizeof(ifo->requestmask6)) {
3140*9f20bfa6SDavid van Moolenbroek for (dhc = dhcp_compats; dhc->dhcp_opt; dhc++) {
3141*9f20bfa6SDavid van Moolenbroek if (has_option_mask(ifo->requestmask, dhc->dhcp_opt))
3142*9f20bfa6SDavid van Moolenbroek add_option_mask(ifo->requestmask6,
3143*9f20bfa6SDavid van Moolenbroek dhc->dhcp6_opt);
3144*9f20bfa6SDavid van Moolenbroek }
3145*9f20bfa6SDavid van Moolenbroek if (ifo->fqdn != FQDN_DISABLE ||
3146*9f20bfa6SDavid van Moolenbroek ifo->options & DHCPCD_HOSTNAME)
3147*9f20bfa6SDavid van Moolenbroek add_option_mask(ifo->requestmask6, D6_OPTION_FQDN);
3148*9f20bfa6SDavid van Moolenbroek }
3149*9f20bfa6SDavid van Moolenbroek
3150*9f20bfa6SDavid van Moolenbroek /* Rapid commit won't work with Prefix Delegation Exclusion */
3151*9f20bfa6SDavid van Moolenbroek if (dhcp6_findselfsla(ifp, NULL))
3152*9f20bfa6SDavid van Moolenbroek del_option_mask(ifo->requestmask6, D6_OPTION_RAPID_COMMIT);
3153*9f20bfa6SDavid van Moolenbroek
3154*9f20bfa6SDavid van Moolenbroek if (state->state == DH6S_INFORM) {
3155*9f20bfa6SDavid van Moolenbroek add_option_mask(ifo->requestmask6, D6_OPTION_INFO_REFRESH_TIME);
3156*9f20bfa6SDavid van Moolenbroek dhcp6_startinform(ifp);
3157*9f20bfa6SDavid van Moolenbroek } else {
3158*9f20bfa6SDavid van Moolenbroek del_option_mask(ifo->requestmask6, D6_OPTION_INFO_REFRESH_TIME);
3159*9f20bfa6SDavid van Moolenbroek dhcp6_startinit(ifp);
3160*9f20bfa6SDavid van Moolenbroek }
3161*9f20bfa6SDavid van Moolenbroek }
3162*9f20bfa6SDavid van Moolenbroek
3163*9f20bfa6SDavid van Moolenbroek int
dhcp6_start(struct interface * ifp,enum DH6S init_state)3164*9f20bfa6SDavid van Moolenbroek dhcp6_start(struct interface *ifp, enum DH6S init_state)
3165*9f20bfa6SDavid van Moolenbroek {
3166*9f20bfa6SDavid van Moolenbroek struct dhcp6_state *state;
3167*9f20bfa6SDavid van Moolenbroek
3168*9f20bfa6SDavid van Moolenbroek state = D6_STATE(ifp);
3169*9f20bfa6SDavid van Moolenbroek if (state) {
3170*9f20bfa6SDavid van Moolenbroek if (state->state == DH6S_INFORMED &&
3171*9f20bfa6SDavid van Moolenbroek init_state == DH6S_INFORM)
3172*9f20bfa6SDavid van Moolenbroek {
3173*9f20bfa6SDavid van Moolenbroek dhcp6_startinform(ifp);
3174*9f20bfa6SDavid van Moolenbroek return 0;
3175*9f20bfa6SDavid van Moolenbroek }
3176*9f20bfa6SDavid van Moolenbroek if (init_state == DH6S_INIT &&
3177*9f20bfa6SDavid van Moolenbroek ifp->options->options & DHCPCD_DHCP6 &&
3178*9f20bfa6SDavid van Moolenbroek (state->state == DH6S_INFORM ||
3179*9f20bfa6SDavid van Moolenbroek state->state == DH6S_INFORMED ||
3180*9f20bfa6SDavid van Moolenbroek state->state == DH6S_DELEGATED))
3181*9f20bfa6SDavid van Moolenbroek {
3182*9f20bfa6SDavid van Moolenbroek /* Change from stateless to stateful */
3183*9f20bfa6SDavid van Moolenbroek goto gogogo;
3184*9f20bfa6SDavid van Moolenbroek }
3185*9f20bfa6SDavid van Moolenbroek /* We're already running DHCP6 */
3186*9f20bfa6SDavid van Moolenbroek /* XXX: What if the managed flag vanishes from all RA? */
3187*9f20bfa6SDavid van Moolenbroek return 0;
3188*9f20bfa6SDavid van Moolenbroek }
3189*9f20bfa6SDavid van Moolenbroek
3190*9f20bfa6SDavid van Moolenbroek if (!(ifp->options->options & DHCPCD_DHCP6))
3191*9f20bfa6SDavid van Moolenbroek return 0;
3192*9f20bfa6SDavid van Moolenbroek
3193*9f20bfa6SDavid van Moolenbroek if (ifp->ctx->ipv6->dhcp_fd == -1 && dhcp6_open(ifp->ctx) == -1)
3194*9f20bfa6SDavid van Moolenbroek return -1;
3195*9f20bfa6SDavid van Moolenbroek
3196*9f20bfa6SDavid van Moolenbroek ifp->if_data[IF_DATA_DHCP6] = calloc(1, sizeof(*state));
3197*9f20bfa6SDavid van Moolenbroek state = D6_STATE(ifp);
3198*9f20bfa6SDavid van Moolenbroek if (state == NULL)
3199*9f20bfa6SDavid van Moolenbroek return -1;
3200*9f20bfa6SDavid van Moolenbroek
3201*9f20bfa6SDavid van Moolenbroek state->sol_max_rt = SOL_MAX_RT;
3202*9f20bfa6SDavid van Moolenbroek state->inf_max_rt = INF_MAX_RT;
3203*9f20bfa6SDavid van Moolenbroek TAILQ_INIT(&state->addrs);
3204*9f20bfa6SDavid van Moolenbroek
3205*9f20bfa6SDavid van Moolenbroek gogogo:
3206*9f20bfa6SDavid van Moolenbroek state->state = init_state;
3207*9f20bfa6SDavid van Moolenbroek dhcp_set_leasefile(state->leasefile, sizeof(state->leasefile),
3208*9f20bfa6SDavid van Moolenbroek AF_INET6, ifp);
3209*9f20bfa6SDavid van Moolenbroek if (ipv6_linklocal(ifp) == NULL) {
3210*9f20bfa6SDavid van Moolenbroek logger(ifp->ctx, LOG_DEBUG,
3211*9f20bfa6SDavid van Moolenbroek "%s: delaying DHCPv6 soliciation for LL address",
3212*9f20bfa6SDavid van Moolenbroek ifp->name);
3213*9f20bfa6SDavid van Moolenbroek ipv6_addlinklocalcallback(ifp, dhcp6_start1, ifp);
3214*9f20bfa6SDavid van Moolenbroek return 0;
3215*9f20bfa6SDavid van Moolenbroek }
3216*9f20bfa6SDavid van Moolenbroek
3217*9f20bfa6SDavid van Moolenbroek dhcp6_start1(ifp);
3218*9f20bfa6SDavid van Moolenbroek return 0;
3219*9f20bfa6SDavid van Moolenbroek }
3220*9f20bfa6SDavid van Moolenbroek
3221*9f20bfa6SDavid van Moolenbroek void
dhcp6_reboot(struct interface * ifp)3222*9f20bfa6SDavid van Moolenbroek dhcp6_reboot(struct interface *ifp)
3223*9f20bfa6SDavid van Moolenbroek {
3224*9f20bfa6SDavid van Moolenbroek struct dhcp6_state *state;
3225*9f20bfa6SDavid van Moolenbroek
3226*9f20bfa6SDavid van Moolenbroek state = D6_STATE(ifp);
3227*9f20bfa6SDavid van Moolenbroek if (state) {
3228*9f20bfa6SDavid van Moolenbroek switch (state->state) {
3229*9f20bfa6SDavid van Moolenbroek case DH6S_BOUND:
3230*9f20bfa6SDavid van Moolenbroek dhcp6_startrebind(ifp);
3231*9f20bfa6SDavid van Moolenbroek break;
3232*9f20bfa6SDavid van Moolenbroek case DH6S_INFORMED:
3233*9f20bfa6SDavid van Moolenbroek dhcp6_startinform(ifp);
3234*9f20bfa6SDavid van Moolenbroek break;
3235*9f20bfa6SDavid van Moolenbroek default:
3236*9f20bfa6SDavid van Moolenbroek dhcp6_startdiscover(ifp);
3237*9f20bfa6SDavid van Moolenbroek break;
3238*9f20bfa6SDavid van Moolenbroek }
3239*9f20bfa6SDavid van Moolenbroek }
3240*9f20bfa6SDavid van Moolenbroek }
3241*9f20bfa6SDavid van Moolenbroek
3242*9f20bfa6SDavid van Moolenbroek static void
dhcp6_freedrop(struct interface * ifp,int drop,const char * reason)3243*9f20bfa6SDavid van Moolenbroek dhcp6_freedrop(struct interface *ifp, int drop, const char *reason)
3244*9f20bfa6SDavid van Moolenbroek {
3245*9f20bfa6SDavid van Moolenbroek struct dhcp6_state *state;
3246*9f20bfa6SDavid van Moolenbroek struct dhcpcd_ctx *ctx;
3247*9f20bfa6SDavid van Moolenbroek unsigned long long options;
3248*9f20bfa6SDavid van Moolenbroek int dropdele;
3249*9f20bfa6SDavid van Moolenbroek
3250*9f20bfa6SDavid van Moolenbroek /*
3251*9f20bfa6SDavid van Moolenbroek * As the interface is going away from dhcpcd we need to
3252*9f20bfa6SDavid van Moolenbroek * remove the delegated addresses, otherwise we lose track
3253*9f20bfa6SDavid van Moolenbroek * of which interface is delegating as we remeber it by pointer.
3254*9f20bfa6SDavid van Moolenbroek * So if we need to change this behaviour, we need to change
3255*9f20bfa6SDavid van Moolenbroek * how we remember which interface delegated.
3256*9f20bfa6SDavid van Moolenbroek *
3257*9f20bfa6SDavid van Moolenbroek * XXX The below is no longer true due to the change of the
3258*9f20bfa6SDavid van Moolenbroek * default IAID, but do PPP links have stable ethernet
3259*9f20bfa6SDavid van Moolenbroek * addresses?
3260*9f20bfa6SDavid van Moolenbroek *
3261*9f20bfa6SDavid van Moolenbroek * To make it more interesting, on some OS's with PPP links
3262*9f20bfa6SDavid van Moolenbroek * there is no guarantee the delegating interface will have
3263*9f20bfa6SDavid van Moolenbroek * the same name or index so think very hard before changing
3264*9f20bfa6SDavid van Moolenbroek * this.
3265*9f20bfa6SDavid van Moolenbroek */
3266*9f20bfa6SDavid van Moolenbroek if (ifp->options)
3267*9f20bfa6SDavid van Moolenbroek options = ifp->options->options;
3268*9f20bfa6SDavid van Moolenbroek else
3269*9f20bfa6SDavid van Moolenbroek options = 0;
3270*9f20bfa6SDavid van Moolenbroek dropdele = (options & (DHCPCD_STOPPING | DHCPCD_RELEASE) &&
3271*9f20bfa6SDavid van Moolenbroek (options & DHCPCD_NODROP) != DHCPCD_NODROP);
3272*9f20bfa6SDavid van Moolenbroek
3273*9f20bfa6SDavid van Moolenbroek if (ifp->ctx->eloop)
3274*9f20bfa6SDavid van Moolenbroek eloop_timeout_delete(ifp->ctx->eloop, NULL, ifp);
3275*9f20bfa6SDavid van Moolenbroek
3276*9f20bfa6SDavid van Moolenbroek if (dropdele)
3277*9f20bfa6SDavid van Moolenbroek dhcp6_delete_delegates(ifp);
3278*9f20bfa6SDavid van Moolenbroek
3279*9f20bfa6SDavid van Moolenbroek state = D6_STATE(ifp);
3280*9f20bfa6SDavid van Moolenbroek if (state) {
3281*9f20bfa6SDavid van Moolenbroek /* Failure to send the release may cause this function to
3282*9f20bfa6SDavid van Moolenbroek * re-enter */
3283*9f20bfa6SDavid van Moolenbroek if (state->state == DH6S_RELEASE) {
3284*9f20bfa6SDavid van Moolenbroek dhcp6_finishrelease(ifp);
3285*9f20bfa6SDavid van Moolenbroek return;
3286*9f20bfa6SDavid van Moolenbroek }
3287*9f20bfa6SDavid van Moolenbroek
3288*9f20bfa6SDavid van Moolenbroek if (drop && options & DHCPCD_RELEASE) {
3289*9f20bfa6SDavid van Moolenbroek if (ifp->carrier == LINK_UP &&
3290*9f20bfa6SDavid van Moolenbroek state->state != DH6S_RELEASED)
3291*9f20bfa6SDavid van Moolenbroek {
3292*9f20bfa6SDavid van Moolenbroek dhcp6_startrelease(ifp);
3293*9f20bfa6SDavid van Moolenbroek return;
3294*9f20bfa6SDavid van Moolenbroek }
3295*9f20bfa6SDavid van Moolenbroek unlink(state->leasefile);
3296*9f20bfa6SDavid van Moolenbroek }
3297*9f20bfa6SDavid van Moolenbroek dhcp6_freedrop_addrs(ifp, drop, NULL);
3298*9f20bfa6SDavid van Moolenbroek free(state->old);
3299*9f20bfa6SDavid van Moolenbroek state->old = state->new;
3300*9f20bfa6SDavid van Moolenbroek state->old_len = state->new_len;
3301*9f20bfa6SDavid van Moolenbroek state->new = NULL;
3302*9f20bfa6SDavid van Moolenbroek state->new_len = 0;
3303*9f20bfa6SDavid van Moolenbroek if (drop && state->old &&
3304*9f20bfa6SDavid van Moolenbroek (options & DHCPCD_NODROP) != DHCPCD_NODROP)
3305*9f20bfa6SDavid van Moolenbroek {
3306*9f20bfa6SDavid van Moolenbroek if (reason == NULL)
3307*9f20bfa6SDavid van Moolenbroek reason = "STOP6";
3308*9f20bfa6SDavid van Moolenbroek script_runreason(ifp, reason);
3309*9f20bfa6SDavid van Moolenbroek }
3310*9f20bfa6SDavid van Moolenbroek free(state->old);
3311*9f20bfa6SDavid van Moolenbroek free(state->send);
3312*9f20bfa6SDavid van Moolenbroek free(state->recv);
3313*9f20bfa6SDavid van Moolenbroek free(state);
3314*9f20bfa6SDavid van Moolenbroek ifp->if_data[IF_DATA_DHCP6] = NULL;
3315*9f20bfa6SDavid van Moolenbroek }
3316*9f20bfa6SDavid van Moolenbroek
3317*9f20bfa6SDavid van Moolenbroek /* If we don't have any more DHCP6 enabled interfaces,
3318*9f20bfa6SDavid van Moolenbroek * close the global socket and release resources */
3319*9f20bfa6SDavid van Moolenbroek ctx = ifp->ctx;
3320*9f20bfa6SDavid van Moolenbroek if (ctx->ifaces) {
3321*9f20bfa6SDavid van Moolenbroek TAILQ_FOREACH(ifp, ctx->ifaces, next) {
3322*9f20bfa6SDavid van Moolenbroek if (D6_STATE(ifp))
3323*9f20bfa6SDavid van Moolenbroek break;
3324*9f20bfa6SDavid van Moolenbroek }
3325*9f20bfa6SDavid van Moolenbroek }
3326*9f20bfa6SDavid van Moolenbroek if (ifp == NULL && ctx->ipv6) {
3327*9f20bfa6SDavid van Moolenbroek if (ctx->ipv6->dhcp_fd != -1) {
3328*9f20bfa6SDavid van Moolenbroek eloop_event_delete(ctx->eloop, ctx->ipv6->dhcp_fd);
3329*9f20bfa6SDavid van Moolenbroek close(ctx->ipv6->dhcp_fd);
3330*9f20bfa6SDavid van Moolenbroek ctx->ipv6->dhcp_fd = -1;
3331*9f20bfa6SDavid van Moolenbroek }
3332*9f20bfa6SDavid van Moolenbroek }
3333*9f20bfa6SDavid van Moolenbroek }
3334*9f20bfa6SDavid van Moolenbroek
3335*9f20bfa6SDavid van Moolenbroek void
dhcp6_drop(struct interface * ifp,const char * reason)3336*9f20bfa6SDavid van Moolenbroek dhcp6_drop(struct interface *ifp, const char *reason)
3337*9f20bfa6SDavid van Moolenbroek {
3338*9f20bfa6SDavid van Moolenbroek
3339*9f20bfa6SDavid van Moolenbroek dhcp6_freedrop(ifp, 1, reason);
3340*9f20bfa6SDavid van Moolenbroek }
3341*9f20bfa6SDavid van Moolenbroek
3342*9f20bfa6SDavid van Moolenbroek void
dhcp6_free(struct interface * ifp)3343*9f20bfa6SDavid van Moolenbroek dhcp6_free(struct interface *ifp)
3344*9f20bfa6SDavid van Moolenbroek {
3345*9f20bfa6SDavid van Moolenbroek
3346*9f20bfa6SDavid van Moolenbroek dhcp6_freedrop(ifp, 0, NULL);
3347*9f20bfa6SDavid van Moolenbroek }
3348*9f20bfa6SDavid van Moolenbroek
3349*9f20bfa6SDavid van Moolenbroek void
dhcp6_handleifa(struct dhcpcd_ctx * ctx,int cmd,const char * ifname,const struct in6_addr * addr,int flags)3350*9f20bfa6SDavid van Moolenbroek dhcp6_handleifa(struct dhcpcd_ctx *ctx, int cmd, const char *ifname,
3351*9f20bfa6SDavid van Moolenbroek const struct in6_addr *addr, int flags)
3352*9f20bfa6SDavid van Moolenbroek {
3353*9f20bfa6SDavid van Moolenbroek struct interface *ifp;
3354*9f20bfa6SDavid van Moolenbroek struct dhcp6_state *state;
3355*9f20bfa6SDavid van Moolenbroek
3356*9f20bfa6SDavid van Moolenbroek if (ctx->ifaces == NULL)
3357*9f20bfa6SDavid van Moolenbroek return;
3358*9f20bfa6SDavid van Moolenbroek
3359*9f20bfa6SDavid van Moolenbroek TAILQ_FOREACH(ifp, ctx->ifaces, next) {
3360*9f20bfa6SDavid van Moolenbroek state = D6_STATE(ifp);
3361*9f20bfa6SDavid van Moolenbroek if (state == NULL || strcmp(ifp->name, ifname))
3362*9f20bfa6SDavid van Moolenbroek continue;
3363*9f20bfa6SDavid van Moolenbroek ipv6_handleifa_addrs(cmd, &state->addrs, addr, flags);
3364*9f20bfa6SDavid van Moolenbroek }
3365*9f20bfa6SDavid van Moolenbroek
3366*9f20bfa6SDavid van Moolenbroek }
3367*9f20bfa6SDavid van Moolenbroek
3368*9f20bfa6SDavid van Moolenbroek ssize_t
dhcp6_env(char ** env,const char * prefix,const struct interface * ifp,const struct dhcp6_message * m,size_t len)3369*9f20bfa6SDavid van Moolenbroek dhcp6_env(char **env, const char *prefix, const struct interface *ifp,
3370*9f20bfa6SDavid van Moolenbroek const struct dhcp6_message *m, size_t len)
3371*9f20bfa6SDavid van Moolenbroek {
3372*9f20bfa6SDavid van Moolenbroek const struct if_options *ifo;
3373*9f20bfa6SDavid van Moolenbroek struct dhcp_opt *opt, *vo;
3374*9f20bfa6SDavid van Moolenbroek const struct dhcp6_option *o;
3375*9f20bfa6SDavid van Moolenbroek size_t i, n;
3376*9f20bfa6SDavid van Moolenbroek uint16_t ol, oc;
3377*9f20bfa6SDavid van Moolenbroek char *pfx;
3378*9f20bfa6SDavid van Moolenbroek uint32_t en;
3379*9f20bfa6SDavid van Moolenbroek const struct dhcpcd_ctx *ctx;
3380*9f20bfa6SDavid van Moolenbroek const struct dhcp6_state *state;
3381*9f20bfa6SDavid van Moolenbroek const struct ipv6_addr *ap;
3382*9f20bfa6SDavid van Moolenbroek char *v, *val;
3383*9f20bfa6SDavid van Moolenbroek
3384*9f20bfa6SDavid van Moolenbroek n = 0;
3385*9f20bfa6SDavid van Moolenbroek if (m == NULL)
3386*9f20bfa6SDavid van Moolenbroek goto delegated;
3387*9f20bfa6SDavid van Moolenbroek
3388*9f20bfa6SDavid van Moolenbroek if (len < sizeof(*m)) {
3389*9f20bfa6SDavid van Moolenbroek /* Should be impossible with guards at packet in
3390*9f20bfa6SDavid van Moolenbroek * and reading leases */
3391*9f20bfa6SDavid van Moolenbroek errno = EINVAL;
3392*9f20bfa6SDavid van Moolenbroek return -1;
3393*9f20bfa6SDavid van Moolenbroek }
3394*9f20bfa6SDavid van Moolenbroek
3395*9f20bfa6SDavid van Moolenbroek ifo = ifp->options;
3396*9f20bfa6SDavid van Moolenbroek ctx = ifp->ctx;
3397*9f20bfa6SDavid van Moolenbroek
3398*9f20bfa6SDavid van Moolenbroek /* Zero our indexes */
3399*9f20bfa6SDavid van Moolenbroek if (env) {
3400*9f20bfa6SDavid van Moolenbroek for (i = 0, opt = ctx->dhcp6_opts;
3401*9f20bfa6SDavid van Moolenbroek i < ctx->dhcp6_opts_len;
3402*9f20bfa6SDavid van Moolenbroek i++, opt++)
3403*9f20bfa6SDavid van Moolenbroek dhcp_zero_index(opt);
3404*9f20bfa6SDavid van Moolenbroek for (i = 0, opt = ifp->options->dhcp6_override;
3405*9f20bfa6SDavid van Moolenbroek i < ifp->options->dhcp6_override_len;
3406*9f20bfa6SDavid van Moolenbroek i++, opt++)
3407*9f20bfa6SDavid van Moolenbroek dhcp_zero_index(opt);
3408*9f20bfa6SDavid van Moolenbroek for (i = 0, opt = ctx->vivso;
3409*9f20bfa6SDavid van Moolenbroek i < ctx->vivso_len;
3410*9f20bfa6SDavid van Moolenbroek i++, opt++)
3411*9f20bfa6SDavid van Moolenbroek dhcp_zero_index(opt);
3412*9f20bfa6SDavid van Moolenbroek i = strlen(prefix) + strlen("_dhcp6") + 1;
3413*9f20bfa6SDavid van Moolenbroek pfx = malloc(i);
3414*9f20bfa6SDavid van Moolenbroek if (pfx == NULL) {
3415*9f20bfa6SDavid van Moolenbroek logger(ifp->ctx, LOG_ERR, "%s: %m", __func__);
3416*9f20bfa6SDavid van Moolenbroek return -1;
3417*9f20bfa6SDavid van Moolenbroek }
3418*9f20bfa6SDavid van Moolenbroek snprintf(pfx, i, "%s_dhcp6", prefix);
3419*9f20bfa6SDavid van Moolenbroek } else
3420*9f20bfa6SDavid van Moolenbroek pfx = NULL;
3421*9f20bfa6SDavid van Moolenbroek
3422*9f20bfa6SDavid van Moolenbroek /* Unlike DHCP, DHCPv6 options *may* occur more than once.
3423*9f20bfa6SDavid van Moolenbroek * There is also no provision for option concatenation unlike DHCP. */
3424*9f20bfa6SDavid van Moolenbroek for (o = D6_CFIRST_OPTION(m);
3425*9f20bfa6SDavid van Moolenbroek len > (ssize_t)sizeof(*o);
3426*9f20bfa6SDavid van Moolenbroek o = D6_CNEXT_OPTION(o))
3427*9f20bfa6SDavid van Moolenbroek {
3428*9f20bfa6SDavid van Moolenbroek ol = ntohs(o->len);
3429*9f20bfa6SDavid van Moolenbroek if (sizeof(*o) + ol > len) {
3430*9f20bfa6SDavid van Moolenbroek errno = EINVAL;
3431*9f20bfa6SDavid van Moolenbroek break;
3432*9f20bfa6SDavid van Moolenbroek }
3433*9f20bfa6SDavid van Moolenbroek len -= sizeof(*o) + ol;
3434*9f20bfa6SDavid van Moolenbroek oc = ntohs(o->code);
3435*9f20bfa6SDavid van Moolenbroek if (has_option_mask(ifo->nomask6, oc))
3436*9f20bfa6SDavid van Moolenbroek continue;
3437*9f20bfa6SDavid van Moolenbroek for (i = 0, opt = ifo->dhcp6_override;
3438*9f20bfa6SDavid van Moolenbroek i < ifo->dhcp6_override_len;
3439*9f20bfa6SDavid van Moolenbroek i++, opt++)
3440*9f20bfa6SDavid van Moolenbroek if (opt->option == oc)
3441*9f20bfa6SDavid van Moolenbroek break;
3442*9f20bfa6SDavid van Moolenbroek if (i == ifo->dhcp6_override_len &&
3443*9f20bfa6SDavid van Moolenbroek oc == D6_OPTION_VENDOR_OPTS &&
3444*9f20bfa6SDavid van Moolenbroek ol > sizeof(en))
3445*9f20bfa6SDavid van Moolenbroek {
3446*9f20bfa6SDavid van Moolenbroek memcpy(&en, D6_COPTION_DATA(o), sizeof(en));
3447*9f20bfa6SDavid van Moolenbroek en = ntohl(en);
3448*9f20bfa6SDavid van Moolenbroek vo = vivso_find(en, ifp);
3449*9f20bfa6SDavid van Moolenbroek } else
3450*9f20bfa6SDavid van Moolenbroek vo = NULL;
3451*9f20bfa6SDavid van Moolenbroek if (i == ifo->dhcp6_override_len) {
3452*9f20bfa6SDavid van Moolenbroek for (i = 0, opt = ctx->dhcp6_opts;
3453*9f20bfa6SDavid van Moolenbroek i < ctx->dhcp6_opts_len;
3454*9f20bfa6SDavid van Moolenbroek i++, opt++)
3455*9f20bfa6SDavid van Moolenbroek if (opt->option == oc)
3456*9f20bfa6SDavid van Moolenbroek break;
3457*9f20bfa6SDavid van Moolenbroek if (i == ctx->dhcp6_opts_len)
3458*9f20bfa6SDavid van Moolenbroek opt = NULL;
3459*9f20bfa6SDavid van Moolenbroek }
3460*9f20bfa6SDavid van Moolenbroek if (opt) {
3461*9f20bfa6SDavid van Moolenbroek n += dhcp_envoption(ifp->ctx,
3462*9f20bfa6SDavid van Moolenbroek env == NULL ? NULL : &env[n],
3463*9f20bfa6SDavid van Moolenbroek pfx, ifp->name,
3464*9f20bfa6SDavid van Moolenbroek opt, dhcp6_getoption, D6_COPTION_DATA(o), ol);
3465*9f20bfa6SDavid van Moolenbroek }
3466*9f20bfa6SDavid van Moolenbroek if (vo) {
3467*9f20bfa6SDavid van Moolenbroek n += dhcp_envoption(ifp->ctx,
3468*9f20bfa6SDavid van Moolenbroek env == NULL ? NULL : &env[n],
3469*9f20bfa6SDavid van Moolenbroek pfx, ifp->name,
3470*9f20bfa6SDavid van Moolenbroek vo, dhcp6_getoption,
3471*9f20bfa6SDavid van Moolenbroek D6_COPTION_DATA(o) + sizeof(en),
3472*9f20bfa6SDavid van Moolenbroek ol - sizeof(en));
3473*9f20bfa6SDavid van Moolenbroek }
3474*9f20bfa6SDavid van Moolenbroek }
3475*9f20bfa6SDavid van Moolenbroek free(pfx);
3476*9f20bfa6SDavid van Moolenbroek
3477*9f20bfa6SDavid van Moolenbroek delegated:
3478*9f20bfa6SDavid van Moolenbroek /* Needed for Delegated Prefixes */
3479*9f20bfa6SDavid van Moolenbroek state = D6_CSTATE(ifp);
3480*9f20bfa6SDavid van Moolenbroek i = 0;
3481*9f20bfa6SDavid van Moolenbroek TAILQ_FOREACH(ap, &state->addrs, next) {
3482*9f20bfa6SDavid van Moolenbroek if (ap->delegating_iface) {
3483*9f20bfa6SDavid van Moolenbroek i += strlen(ap->saddr) + 1;
3484*9f20bfa6SDavid van Moolenbroek }
3485*9f20bfa6SDavid van Moolenbroek }
3486*9f20bfa6SDavid van Moolenbroek if (env && i) {
3487*9f20bfa6SDavid van Moolenbroek i += strlen(prefix) + strlen("_delegated_dhcp6_prefix=");
3488*9f20bfa6SDavid van Moolenbroek v = val = env[n] = malloc(i);
3489*9f20bfa6SDavid van Moolenbroek if (v == NULL) {
3490*9f20bfa6SDavid van Moolenbroek logger(ifp->ctx, LOG_ERR, "%s: %m", __func__);
3491*9f20bfa6SDavid van Moolenbroek return -1;
3492*9f20bfa6SDavid van Moolenbroek }
3493*9f20bfa6SDavid van Moolenbroek v += snprintf(val, i, "%s_delegated_dhcp6_prefix=", prefix);
3494*9f20bfa6SDavid van Moolenbroek TAILQ_FOREACH(ap, &state->addrs, next) {
3495*9f20bfa6SDavid van Moolenbroek if (ap->delegating_iface) {
3496*9f20bfa6SDavid van Moolenbroek /* Can't use stpcpy(3) due to "security" */
3497*9f20bfa6SDavid van Moolenbroek const char *sap = ap->saddr;
3498*9f20bfa6SDavid van Moolenbroek
3499*9f20bfa6SDavid van Moolenbroek do
3500*9f20bfa6SDavid van Moolenbroek *v++ = *sap;
3501*9f20bfa6SDavid van Moolenbroek while (*++sap != '\0');
3502*9f20bfa6SDavid van Moolenbroek *v++ = ' ';
3503*9f20bfa6SDavid van Moolenbroek }
3504*9f20bfa6SDavid van Moolenbroek }
3505*9f20bfa6SDavid van Moolenbroek *--v = '\0';
3506*9f20bfa6SDavid van Moolenbroek }
3507*9f20bfa6SDavid van Moolenbroek if (i)
3508*9f20bfa6SDavid van Moolenbroek n++;
3509*9f20bfa6SDavid van Moolenbroek
3510*9f20bfa6SDavid van Moolenbroek return (ssize_t)n;
3511*9f20bfa6SDavid van Moolenbroek }
3512*9f20bfa6SDavid van Moolenbroek
3513*9f20bfa6SDavid van Moolenbroek int
dhcp6_dump(struct interface * ifp)3514*9f20bfa6SDavid van Moolenbroek dhcp6_dump(struct interface *ifp)
3515*9f20bfa6SDavid van Moolenbroek {
3516*9f20bfa6SDavid van Moolenbroek struct dhcp6_state *state;
3517*9f20bfa6SDavid van Moolenbroek
3518*9f20bfa6SDavid van Moolenbroek ifp->if_data[IF_DATA_DHCP6] = state = calloc(1, sizeof(*state));
3519*9f20bfa6SDavid van Moolenbroek if (state == NULL) {
3520*9f20bfa6SDavid van Moolenbroek logger(ifp->ctx, LOG_ERR, "%s: %m", __func__);
3521*9f20bfa6SDavid van Moolenbroek return -1;
3522*9f20bfa6SDavid van Moolenbroek }
3523*9f20bfa6SDavid van Moolenbroek TAILQ_INIT(&state->addrs);
3524*9f20bfa6SDavid van Moolenbroek dhcp_set_leasefile(state->leasefile, sizeof(state->leasefile),
3525*9f20bfa6SDavid van Moolenbroek AF_INET6, ifp);
3526*9f20bfa6SDavid van Moolenbroek if (dhcp6_readlease(ifp, 0) == -1) {
3527*9f20bfa6SDavid van Moolenbroek logger(ifp->ctx, LOG_ERR, "%s: %s: %m",
3528*9f20bfa6SDavid van Moolenbroek *ifp->name ? ifp->name : state->leasefile, __func__);
3529*9f20bfa6SDavid van Moolenbroek return -1;
3530*9f20bfa6SDavid van Moolenbroek }
3531*9f20bfa6SDavid van Moolenbroek state->reason = "DUMP6";
3532*9f20bfa6SDavid van Moolenbroek return script_runreason(ifp, state->reason);
3533*9f20bfa6SDavid van Moolenbroek }
3534