This is automatically started when RA flags O is set.
If no DHCPv6 request options are configured in dhcpcd.conf then we
request domain servers and search lists.
PROG= dhcpcd
SRCS= arp.c bind.c common.c control.c dhcp.c dhcpcd.c duid.c eloop.c
SRCS+= configure.c if-options.c if-pref.c ipv4ll.c net.c signals.c
-SRCS+= ipv6.c ipv6rs.c ipv6ns.c
+SRCS+= ipv6.c ipv6rs.c ipv6ns.c dhcp6.c
CFLAGS?= -O2
CSTD?= c99
#include "common.h"
#include "configure.h"
#include "dhcp.h"
+#include "dhcp6.h"
#include "if-options.h"
#include "if-pref.h"
#include "ipv6rs.h"
static struct rt *routes;
+static const char *if_params[] = {
+ "interface",
+ "reason",
+ "pid",
+ "ifmetric",
+ "ifwireless",
+ "ifflags",
+ "ssid",
+ "profile",
+ "interface_order",
+ NULL
+};
+
+void
+if_printoptions(void)
+{
+ const char **p;
+
+ for (p = if_params; *p; p++)
+ printf(" - %s\n", *p);
+}
+
static int
exec_script(char *const *argv, char *const *env)
{
ssize_t e, elen, l;
const struct if_options *ifo = iface->state->options;
const struct interface *ifp;
- int dhcp, ra;
+ int dhcp, dhcp6, ra;
+ const struct dhcp6_state *d6_state;
- dhcp = ra = 0;
+ dhcp = dhcp6 = ra = 0;
+ d6_state = D6_STATE(iface);
if (strcmp(reason, "TEST") == 0) {
- if (ipv6rs_has_ra(iface))
+ if (d6_state && d6_state->new)
+ dhcp6 = 1;
+ else if (ipv6rs_has_ra(iface))
ra = 1;
else
dhcp = 1;
- } else if (strcmp(reason, "ROUTERADVERT") == 0)
+ } else if (reason[strlen(reason) - 1] == '6')
+ dhcp6 = 1;
+ else if (strcmp(reason, "ROUTERADVERT") == 0)
ra = 1;
else
dhcp = 1;
if (strcmp(reason, "TEST") == 0) {
env[8] = strdup("if_up=false");
env[9] = strdup("if_down=false");
- } else if ((dhcp && iface->state->new) || (ra && ipv6rs_has_ra(iface))){
+ } else if ((dhcp && iface->state->new) ||
+ (dhcp6 && d6_state->new) ||
+ (ra && ipv6rs_has_ra(iface)))
+ {
env[8] = strdup("if_up=true");
env[9] = strdup("if_down=false");
} else {
append_config(&env, &elen, "old",
(const char *const *)ifo->config);
}
+ if (dhcp6 && d6_state->old) {
+ e = dhcp6_env(NULL, NULL, iface,
+ d6_state->old, d6_state->old_len);
+ if (e > 0) {
+ env = xrealloc(env, sizeof(char *) * (elen + e + 1));
+ elen += dhcp6_env(env + elen, "old", iface,
+ d6_state->old, d6_state->old_len);
+ }
+ }
dumplease:
if (dhcp && iface->state->new) {
append_config(&env, &elen, "new",
(const char *const *)ifo->config);
}
+ if (dhcp6 && d6_state->new) {
+ e = dhcp6_env(NULL, NULL, iface,
+ d6_state->new, d6_state->new_len);
+ if (e > 0) {
+ env = xrealloc(env, sizeof(char *) * (elen + e + 1));
+ elen += dhcp6_env(env + elen, "new", iface,
+ d6_state->new, d6_state->new_len);
+ }
+ }
if (ra) {
e = ipv6rs_env(NULL, NULL, iface);
if (e > 0) {
#include "net.h"
+void if_printoptions(void);
int send_interface(int, const struct interface *);
int run_script_reason(const struct interface *, const char *);
void build_routes(void);
#define CONFIG_H
#define PACKAGE "dhcpcd"
-#define VERSION "5.6.2"
+#define VERSION "5.99.1"
#ifndef CONFIG
# define CONFIG SYSCONFDIR "/" PACKAGE ".conf"
#include "common.h"
#include "dhcp.h"
-#define REQUEST (1 << 0)
-#define UINT8 (1 << 1)
-#define UINT16 (1 << 2)
-#define SINT16 (1 << 3)
-#define UINT32 (1 << 4)
-#define SINT32 (1 << 5)
-#define IPV4 (1 << 6)
-#define STRING (1 << 7)
-#define PAIR (1 << 8)
-#define ARRAY (1 << 9)
-#define RFC3361 (1 << 10)
-#define RFC3397 (1 << 11)
-#define RFC3442 (1 << 12)
-#define RFC5969 (1 << 13)
-
-#define IPV4R IPV4 | REQUEST
-
#define DAD "Duplicate address detected"
/* Our aggregate option buffer.
* practically never. See RFC3396 for details. */
static uint8_t *opt_buffer;
-struct dhcp_opt {
- uint8_t option;
- int type;
- const char *var;
-};
-
-static const struct dhcp_opt const dhcp_opts[] = {
+const struct dhcp_opt const dhcp_opts[] = {
{ 1, IPV4 | REQUEST, "subnet_mask" },
/* RFC 3442 states that the CSR has to come before all other
* routes. For completeness, we also specify static routes,
{ 0, 0, NULL }
};
-static const char *if_params[] = {
- "interface",
- "reason",
- "pid",
- "ifmetric",
- "ifwireless",
- "ifflags",
- "profile",
- "interface_order",
- NULL
-};
-
static const char *dhcp_params[] = {
"ip_address",
"subnet_cidr",
"network_number",
- "ssid",
"filename",
"server_name",
NULL
const struct dhcp_opt *opt;
const char **p;
- for (p = if_params; *p; p++)
- printf(" - %s\n", *p);
-
for (p = dhcp_params; *p; p++)
printf(" %s\n", *p);
printf("%03d %s\n", opt->option, opt->var);
}
-int make_option_mask(uint8_t *mask, const char *opts, int add)
+int make_option_mask(const struct dhcp_opt *dopts,
+ uint8_t *mask, const char *opts, int add)
{
char *token, *o, *p, *t;
const struct dhcp_opt *opt;
while ((token = strsep(&p, ", "))) {
if (*token == '\0')
continue;
- for (opt = dhcp_opts; opt->option; opt++) {
+ for (opt = dopts; opt->option; opt++) {
if (!opt->var)
continue;
match = 0;
return dhcp;
}
-static ssize_t
+ssize_t
print_string(char *s, ssize_t len, int dl, const uint8_t *data)
{
uint8_t c;
return bytes;
}
-static ssize_t
+ssize_t
print_option(char *s, ssize_t len, int type, int dl, const uint8_t *data)
{
const uint8_t *e, *t;
struct in_addr addr;
ssize_t bytes = 0;
ssize_t l;
- char *tmp;
+ char *tmp, ntopbuf[INET6_ADDRSTRLEN];
+ const char *addr6;
if (type & RFC3397) {
l = decode_rfc3397(NULL, 0, dl, data);
return print_string(s, len, dl, data);
}
+ /* DHCPv6 status code */
+ if (type & SCODE && dl >= (int)sizeof(u16)) {
+ if (s) {
+ memcpy(&u16, data, sizeof(u16));
+ u16 = ntohs(u16);
+ l = snprintf(s, len, "%d ", u16);
+ len -= l;
+ } else
+ l = 7;
+ data += sizeof(u16);
+ dl -= sizeof(u16);
+ if (dl)
+ l += print_option(s, len, STRING, dl, data);
+ return l;
+ }
+
if (!s) {
if (type & UINT8)
l = 3;
} else if (type & IPV4) {
l = 16;
dl /= 4;
+ } else if (type & IPV6) {
+ l = INET6_ADDRSTRLEN;
+ dl /= 16;
+ } else if (type & BINHEX) {
+ l = 2;
} else {
errno = EINVAL;
return -1;
t = data;
e = data + dl;
while (data < e) {
- if (data != t) {
+ if (data != t && type != BINHEX) {
*s++ = ' ';
bytes++;
len--;
memcpy(&addr.s_addr, data, sizeof(addr.s_addr));
l = snprintf(s, len, "%s", inet_ntoa(addr));
data += sizeof(addr.s_addr);
+ } else if (type & IPV6) {
+ addr6 = inet_ntop(AF_INET6, data,
+ ntopbuf, sizeof(ntopbuf));
+ l = snprintf(s, len, "%s", addr6);
+ data += 16;
+ } else if (type & BINHEX) {
+ l = snprintf(s, len, "%.2x", data[0]);
+ data++;
} else
l = 0;
len -= l;
DHO_END = 255
};
+#define REQUEST (1 << 0)
+#define UINT8 (1 << 1)
+#define UINT16 (1 << 2)
+#define SINT16 (1 << 3)
+#define UINT32 (1 << 4)
+#define SINT32 (1 << 5)
+#define IPV4 (1 << 6)
+#define STRING (1 << 7)
+#define PAIR (1 << 8)
+#define ARRAY (1 << 9)
+#define RFC3361 (1 << 10)
+#define RFC3397 (1 << 11)
+#define RFC3442 (1 << 12)
+#define RFC5969 (1 << 13)
+#define IPV6 (1 << 14)
+#define BINHEX (1 << 15)
+#define SCODE (1 << 16)
+
+#define IPV4R IPV4 | REQUEST
+
/* FQDN values - lsnybble used in flags
* hsnybble to create order
* and to allow 0x00 to mean disable
#include "if-options.h"
#include "net.h"
+struct dhcp_opt {
+ uint16_t option;
+ int type;
+ const char *var;
+};
+
+extern const struct dhcp_opt const dhcp_opts[];
+
#define add_option_mask(var, val) (var[val >> 3] |= 1 << (val & 7))
#define del_option_mask(var, val) (var[val >> 3] &= ~(1 << (val & 7)))
#define has_option_mask(var, val) (var[val >> 3] & (1 << (val & 7)))
-int make_option_mask(uint8_t *, const char *, int);
+int make_option_mask(const struct dhcp_opt *,uint8_t *, const char *, int);
void print_options(void);
char *get_option_string(const struct dhcp_message *, uint8_t);
int get_option_addr(struct in_addr *, const struct dhcp_message *, uint8_t);
struct rt *get_option_routes(const struct dhcp_message *, const char *,
unsigned long long *);
ssize_t decode_rfc3397(char *, ssize_t, int, const uint8_t *);
+ssize_t print_string(char *s, ssize_t len, int dl, const uint8_t *data);
+ssize_t print_option(char *s, ssize_t len, int type, int dl,
+ const uint8_t *data);
ssize_t configure_env(char **, const char *, const struct dhcp_message *,
const struct if_options *);
--- /dev/null
+/*
+ * dhcpcd - DHCP client daemon
+ * Copyright (c) 2006-2012 Roy Marples <roy@marples.name>
+ * All rights reserved
+
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/utsname.h>
+
+#include <netinet/in.h>
+#ifdef __linux__
+# define _LINUX_IN6_H
+# include <linux/ipv6.h>
+#endif
+
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <unistd.h>
+
+#define ELOOP_QUEUE 2
+
+#include "config.h"
+#include "common.h"
+#include "configure.h"
+#include "dhcp.h"
+#include "dhcp6.h"
+#include "duid.h"
+#include "eloop.h"
+#include "platform.h"
+
+#ifndef __UNCONST
+#define __UNCONST(a) ((void *)(unsigned long)(const void *)(a))
+#endif
+
+/*
+ * We presently don't have an IANA PEN, although I have applied for one.
+ * This means we can't really identify ourselves to a DHCPv6 server
+ * unlike our DHCPv4 stack.
+ * This blows chunks, but you maybe able to use your own or the
+ * IANA reserved one of 0.
+ */
+//#define DHCPCD_IANA_PEN 0
+
+/* Unsure if I want this */
+//#define VENDOR_SPLIT
+
+static int sock = -1;
+static struct sockaddr_in6 allrouters, from;
+static struct msghdr sndhdr;
+static struct iovec sndiov[2];
+static unsigned char *sndbuf;
+static struct msghdr rcvhdr;
+static struct iovec rcviov[2];
+static unsigned char *rcvbuf;
+static unsigned char ansbuf[1500];
+static unsigned char *duid;
+static uint16_t duid_len;
+static char ntopbuf[INET6_ADDRSTRLEN];
+
+struct dhcp6_op {
+ uint16_t type;
+ const char *name;
+};
+
+static const struct dhcp6_op dhcp6_ops[] = {
+ { DHCP6_SOLICIT, "SOLICIT6" },
+ { DHCP6_REQUEST, "REQUEST6" },
+ { DHCP6_REPLY, "REPLY6" },
+ { DHCP6_INFORMATION_REQ, "INFORM6" },
+ { 0, NULL }
+};
+
+const struct dhcp_opt const dhcp6_opts[] = {
+ { D6_OPTION_CLIENTID, BINHEX, "client_id" },
+ { D6_OPTION_SERVERID, BINHEX, "server_id" },
+ { D6_OPTION_IA_ADDR, IPV6 | ARRAY, "ia_addr" },
+ { D6_OPTION_PREFERENCE, UINT8, "preference" },
+ { D6_OPTION_RAPID_COMMIT, 0, "rapid_commit" },
+ { D6_OPTION_UNICAST, IPV6, "unicast" },
+ { D6_OPTION_STATUS_CODE, SCODE, "status_code" },
+ { D6_OPTION_SIP_SERVERS_NAME, RFC3397, "sip_servers_names" },
+ { D6_OPTION_SIP_SERVERS_ADDRESS,IPV6 | ARRAY, "sip_servers_addresses" },
+ { D6_OPTION_DNS_SERVERS, IPV6 | ARRAY, "name_servers" },
+ { D6_OPTION_DOMAIN_LIST, RFC3397, "domain_search" },
+ { D6_OPTION_NIS_SERVERS, IPV6 | ARRAY, "nis_servers" },
+ { D6_OPTION_NISP_SERVERS, IPV6 | ARRAY, "nisp_servers" },
+ { D6_OPTION_NIS_DOMAIN_NAME, RFC3397, "nis_domain_name" },
+ { D6_OPTION_NISP_DOMAIN_NAME, RFC3397, "nisp_domain_name" },
+ { D6_OPTION_SNTP_SERVERS, IPV6 | ARRAY, "sntp_servers" },
+ { D6_OPTION_INFO_REFRESH_TIME, UINT32, "info_refresh_time" },
+ { D6_OPTION_BCMS_SERVER_D, RFC3397, "bcms_server_d" },
+ { D6_OPTION_BCMS_SERVER_A, IPV6 | ARRAY, "bcms_server_a" },
+ { 0, 0, NULL }
+};
+
+#if DEBUG_MEMORY
+static void
+dhcp6_cleanup(void)
+{
+
+ free(sndbuf);
+ free(rcvbuf);
+ free(duid);
+}
+#endif
+
+void
+dhcp6_printoptions(void)
+{
+ const struct dhcp_opt *opt;
+
+ for (opt = dhcp6_opts; opt->option; opt++)
+ if (opt->var)
+ printf("%05d %s\n", opt->option, opt->var);
+}
+
+static int
+dhcp6_init(void)
+{
+ int len;
+
+#if DEBUG_MEMORY
+ atexit(dhcp6_cleanup);
+#endif
+
+ memset(&allrouters, 0, sizeof(allrouters));
+ allrouters.sin6_family = AF_INET6;
+ allrouters.sin6_port = htons(DHCP6_SERVER_PORT);
+#ifdef SIN6_LEN
+ allrouters.sin6_len = sizeof(allrouters);
+#endif
+ if (inet_pton(AF_INET6, ALLROUTERS, &allrouters.sin6_addr.s6_addr) != 1)
+ return -1;
+
+ len = CMSG_SPACE(sizeof(struct in6_pktinfo));
+ sndbuf = calloc(1, len);
+ if (sndbuf == NULL)
+ return -1;
+ sndhdr.msg_namelen = sizeof(struct sockaddr_in6);
+ sndhdr.msg_iov = sndiov;
+ sndhdr.msg_iovlen = 1;
+ sndhdr.msg_control = sndbuf;
+ sndhdr.msg_controllen = len;
+
+ rcvbuf = calloc(1, len);
+ if (rcvbuf == NULL) {
+ free(sndbuf);
+ sndbuf = NULL;
+ return -1;
+ }
+ rcvhdr.msg_name = &from;
+ rcvhdr.msg_namelen = sizeof(from);
+ rcvhdr.msg_iov = rcviov;
+ rcvhdr.msg_iovlen = 1;
+ rcvhdr.msg_control = rcvbuf;
+ rcvhdr.msg_controllen = len;
+ rcviov[0].iov_base = ansbuf;
+ rcviov[0].iov_len = sizeof(ansbuf);
+
+ return 0;
+}
+
+#ifdef DHCPCD_IANA_PEN
+static size_t
+dhcp6_makevendor(struct dhcp6_option *o)
+{
+ size_t len;
+ uint8_t *p;
+ uint16_t u16;
+ uint32_t u32;
+ size_t vlen;
+#ifdef VENDOR_SPLIT
+ const char *platform;
+ size_t plen, unl, url, uml, pl;
+ struct utsname utn;
+#endif
+
+ len = sizeof(uint32_t); /* IANA PEN */
+
+#ifdef VENDOR_SPLIT
+ plen = strlen(PACKAGE);
+ vlen = strlen(VERSION);
+ len += sizeof(uint16_t) + plen + 1 + vlen;
+ if (uname(&utn) == 0) {
+ unl = strlen(utn.sysname);
+ url = strlen(utn.release);
+ uml = strlen(utn.machine);
+ platform = hardware_platform();
+ pl = strlen(platform);
+ len += sizeof(uint16_t) + unl + 1 + url;
+ len += sizeof(uint16_t) + uml;
+ len += sizeof(uint16_t) + pl;
+ } else
+ unl = 0;
+#else
+ vlen = strlen(vendor);
+ len += sizeof(uint16_t) + vlen;
+#endif
+
+ if (o) {
+ o->code = htons(D6_OPTION_VENDOR);
+ o->len = htons(len);
+ p = D6_OPTION_DATA(o);
+ u32 = DHCPCD_IANA_PEN;
+ memcpy(p, &u32, sizeof(u32));
+ p += sizeof(u32);
+#ifdef VENDOR_SPLIT
+ u16 = htons(plen + 1 + vlen);
+ memcpy(p, &u16, sizeof(u16));
+ p += sizeof(u16);
+ memcpy(p, PACKAGE, plen);
+ p += plen;
+ *p++ = '-';
+ memcpy(p, VERSION, vlen);
+ p += vlen;
+ if (unl > 0) {
+ u16 = htons(unl + 1 + url);
+ memcpy(p, &u16, sizeof(u16));
+ p += sizeof(u16);
+ memcpy(p, utn.sysname, unl);
+ p += unl;
+ *p++ = '-';
+ memcpy(p, utn.release, url);
+ p += url;
+ u16 = htons(uml);
+ memcpy(p, &u16, sizeof(u16));
+ p += sizeof(u16);
+ memcpy(p, utn.machine, uml);
+ p += uml;
+ u16 = htons(pl);
+ memcpy(p, &u16, sizeof(u16));
+ p += sizeof(u16);
+ memcpy(p, platform, pl);
+ }
+#else
+ u16 = htons(vlen);
+ memcpy(p, &u16, sizeof(u16));
+ p += sizeof(u16);
+ memcpy(p, vendor, vlen);
+#endif
+ }
+
+ return len;
+}
+#endif
+
+static const struct dhcp6_option *
+dhcp6_getoption(int code, const struct dhcp6_message *m, ssize_t len)
+{
+ const struct dhcp6_option *o;
+
+ code = htons(code);
+ len -= sizeof(*m);
+ for (o = D6_CFIRST_OPTION(m);
+ len > (ssize_t)sizeof(*o);
+ o = D6_CNEXT_OPTION(o))
+ {
+ if (o->len == 0)
+ break;
+ len -= sizeof(*o) + ntohs(o->len);
+ if (len < 0) {
+ errno = EINVAL;
+ return NULL;
+ }
+ if (o->code == code)
+ return o;
+ }
+
+ errno = ESRCH;
+ return NULL;
+}
+
+
+static int
+dhcp6_updateelapsed(struct interface *ifp, struct dhcp6_message *m, ssize_t len)
+{
+ struct dhcp6_state *state;
+ const struct dhcp6_option *co;
+ struct dhcp6_option *o;
+ time_t up;
+ uint16_t u16;
+
+ co = dhcp6_getoption(D6_OPTION_ELAPSED, m, len);
+ if (co == NULL)
+ return -1;
+
+ o = __UNCONST(co);
+ state = D6_STATE(ifp);
+ up = uptime() - state->start_uptime;
+ if (up < 0 || up > (time_t)UINT16_MAX)
+ up = (time_t)UINT16_MAX;
+ u16 = htons(up);
+ memcpy(D6_OPTION_DATA(o), &u16, sizeof(u16));
+ return 0;
+}
+
+static int
+dhcp6_makemessage(struct interface *ifp)
+{
+ struct dhcp6_state *state;
+ struct dhcp6_option *o;
+ int xid;
+ ssize_t len;
+ uint16_t *u16;
+ const struct if_options *ifo;
+ const struct dhcp_opt *opt;
+
+ state = D6_STATE(ifp);
+ if (state->send) {
+ free(state->send);
+ state->send = NULL;
+ }
+
+ /* Work out option size first */
+ ifo = ifp->state->options;
+ len = 0;
+ for (opt = dhcp6_opts; opt->option; opt++) {
+ if (!(opt->type & REQUEST ||
+ has_option_mask(ifo->requestmask6, opt->option)))
+ continue;
+ len += sizeof(*u16);
+ }
+ if (len == 0)
+ len = sizeof(*u16) * 2;
+ len += sizeof(*o);
+
+ len += sizeof(state->send);
+ len += sizeof(*o) + 14; /* clientid */
+ len += sizeof(*o) + sizeof(uint16_t); /* elapsed */
+#ifdef DHCPCD_IANA_PEN
+ len += sizeof(*o) + dhcp6_makevendor(NULL);
+#endif
+
+ state->send = calloc(1, len);
+ if (state->send == NULL)
+ return -1;
+
+ state->send_len = len;
+ if (state->state == DH6S_INFORM)
+ state->send->type = DHCP6_INFORMATION_REQ;
+ else
+ state->send->type = DHCP6_SOLICIT;
+ xid = arc4random();
+ state->send->xid[0] = (xid >> 16) & 0xff;
+ state->send->xid[1] = (xid >> 8) & 0xff;
+ state->send->xid[2] = xid & 0xff;
+
+ o = D6_FIRST_OPTION(state->send);
+ o->code = htons(D6_OPTION_CLIENTID);
+ o->len = htons(duid_len);
+ memcpy(D6_OPTION_DATA(o), duid, duid_len);
+
+ o = D6_NEXT_OPTION(o);
+ o->code = htons(D6_OPTION_ELAPSED);
+ o->len = htons(sizeof(uint16_t));
+ dhcp6_updateelapsed(ifp, state->send, state->send_len);
+
+#ifdef DHCPCD_IANA_PEN
+ o = D6_NEXT_OPTION(o);
+ dhcp6_makevendor(o);
+#endif
+
+ o = D6_NEXT_OPTION(o);
+ o->code = htons(D6_OPTION_ORO);
+ o->len = 0;
+ u16 = (uint16_t *)(void *)D6_OPTION_DATA(o);
+ for (opt = dhcp6_opts; opt->option; opt++) {
+ if (!(opt->type & REQUEST ||
+ has_option_mask(ifo->requestmask6, opt->option)))
+ continue;
+ *u16++ = htons(opt->option);
+ o->len += sizeof(*u16);
+ }
+ if (o->len == 0) {
+ *u16++ = htons(D6_OPTION_DNS_SERVERS);
+ *u16++ = htons(D6_OPTION_DOMAIN_LIST);
+ o->len = sizeof(*u16) * 2;
+ }
+ o->len = htons(o->len);
+
+ return 0;
+}
+
+static const char *
+dhcp6_get_op(uint16_t type)
+{
+ const struct dhcp6_op *d;
+
+ for (d = dhcp6_ops; d->name; d++)
+ if (d->type == type)
+ return d->name;
+ return NULL;
+}
+
+static void
+dhcp6_sendmessage(struct interface *ifp, void (*callback)(void *))
+{
+ struct dhcp6_state *state;
+ struct timeval tv;
+ struct sockaddr_in6 to;
+ struct cmsghdr *cm;
+ struct in6_pktinfo pi;
+
+ state = D6_STATE(ifp);
+ if (!callback)
+ syslog(LOG_DEBUG, "%s: sending %s with xid 0x%02x%02x%02x",
+ ifp->name,
+ dhcp6_get_op(state->send->type),
+ state->send->xid[0],
+ state->send->xid[1],
+ state->send->xid[2]);
+ else {
+ if (state->interval == 0)
+ state->interval = 4;
+ else {
+ state->interval *= 2;
+ if (state->interval > 64)
+ state->interval = 64;
+ }
+ tv.tv_sec = state->interval + DHCP_RAND_MIN;
+ tv.tv_usec = arc4random() % (DHCP_RAND_MAX_U - DHCP_RAND_MIN_U);
+ syslog(LOG_DEBUG,
+ "%s: sending %s (xid 0x%02x%02x%02x), next in %0.2f seconds",
+ ifp->name, dhcp6_get_op(state->send->type),
+ state->send->xid[0],
+ state->send->xid[1],
+ state->send->xid[2],
+ timeval_to_double(&tv));
+ }
+
+ to = allrouters;
+ sndhdr.msg_name = (caddr_t)&to;
+ sndhdr.msg_iov[0].iov_base = (caddr_t)state->send;
+ sndhdr.msg_iov[0].iov_len = state->send_len;
+
+ /* Set the outbound interface */
+ cm = CMSG_FIRSTHDR(&sndhdr);
+ cm->cmsg_level = IPPROTO_IPV6;
+ cm->cmsg_type = IPV6_PKTINFO;
+ cm->cmsg_len = CMSG_LEN(sizeof(pi));
+ memset(&pi, 0, sizeof(pi));
+ pi.ipi6_ifindex = ifp->index;
+ memcpy(CMSG_DATA(cm), &pi, sizeof(pi));
+
+ if (sendmsg(sock, &sndhdr, 0) == -1)
+ syslog(LOG_ERR, "%s: sendmsg: %m", ifp->name);
+
+ if (callback)
+ add_timeout_tv(&tv, callback, ifp);
+}
+
+static void
+dhcp6_sendinform(void *arg)
+{
+
+ dhcp6_sendmessage(arg, dhcp6_sendinform);
+}
+
+/* ARGSUSED */
+static void
+dhcp6_handledata(_unused void *arg)
+{
+ ssize_t len;
+ struct cmsghdr *cm;
+ struct in6_pktinfo pkt;
+ struct interface *ifp;
+ const char *sfrom, *op;
+ struct dhcp6_message *m, *r;
+ struct dhcp6_state *state;
+ const struct dhcp6_option *o;
+ const char *reason;
+ const struct dhcp_opt *opt;
+ const struct if_options *ifo;
+
+ len = recvmsg(sock, &rcvhdr, 0);
+ if (len == -1) {
+ syslog(LOG_ERR, "recvmsg: %m");
+ return;
+ }
+ sfrom = inet_ntop(AF_INET6, &from.sin6_addr,
+ ntopbuf, INET6_ADDRSTRLEN);
+ if ((size_t)len < sizeof(struct dhcp6_message)) {
+ syslog(LOG_ERR, "DHCPv6 RA packet too short from %s", sfrom);
+ return;
+ }
+
+ pkt.ipi6_ifindex = 0;
+ for (cm = (struct cmsghdr *)CMSG_FIRSTHDR(&rcvhdr);
+ cm;
+ cm = (struct cmsghdr *)CMSG_NXTHDR(&rcvhdr, cm))
+ {
+ if (cm->cmsg_level != IPPROTO_IPV6)
+ continue;
+ switch(cm->cmsg_type) {
+ case IPV6_PKTINFO:
+ if (cm->cmsg_len == CMSG_LEN(sizeof(pkt)))
+ memcpy(&pkt, CMSG_DATA(cm), sizeof(pkt));
+ break;
+ }
+ }
+
+ if (pkt.ipi6_ifindex == 0) {
+ syslog(LOG_ERR,
+ "DHCPv6 reply did not contain index from %s",
+ sfrom);
+ return;
+ }
+
+
+ for (ifp = ifaces; ifp; ifp = ifp->next)
+ if (ifp->index == (unsigned int)pkt.ipi6_ifindex)
+ break;
+ if (ifp == NULL) {
+ syslog(LOG_ERR, "DHCPv6 reply for unexpected interface from %s",
+ sfrom);
+ return;
+ }
+ state = D6_STATE(ifp);
+ if (state == NULL || state->send == NULL) {
+ syslog(LOG_ERR, "%s: DHCPv6 reply received but not running",
+ ifp->name);
+ return;
+ }
+
+ m = state->send;
+ r = (struct dhcp6_message *)rcvhdr.msg_iov[0].iov_base;
+ if (r->xid[0] != m->xid[0] ||
+ r->xid[1] != m->xid[1] ||
+ r->xid[2] != m->xid[2])
+ {
+ syslog(LOG_ERR,
+ "%s: wrong xid 0x%02x%02x%02x (expecting 0x%02x%02x%02x) from %s",
+ ifp->name,
+ r->xid[0], r->xid[1], r->xid[2],
+ r->xid[0], r->xid[1], r->xid[2],
+ sfrom);
+ return;
+ }
+
+ if (dhcp6_getoption(D6_OPTION_SERVERID, r, len) == NULL) {
+ syslog(LOG_ERR, "%s: no DHCPv6 server ID from %s",
+ ifp->name, sfrom);
+ return;
+ }
+
+ o = dhcp6_getoption(D6_OPTION_CLIENTID, r, len);
+ if (o && ntohs(o->len) != duid_len &&
+ memcmp(D6_COPTION_DATA(o), duid, duid_len) != 0)
+ {
+ syslog(LOG_ERR, "%s: incorrect client ID from %s",
+ ifp->name, sfrom);
+ return;
+ }
+
+ ifo = ifp->state->options;
+ for (opt = dhcp6_opts; opt->option; opt++) {
+ if (has_option_mask(ifo->requiremask6, opt->option) &&
+ dhcp6_getoption(opt->option, r, len) == NULL)
+ {
+ syslog(LOG_WARNING,
+ "%s: reject DHCPv6 (no option %s) from %s",
+ ifp->name, opt->var, sfrom);
+ return;
+ }
+ }
+
+ m = malloc(len);
+ if (m == NULL) {
+ syslog(LOG_ERR, "%s: malloc DHCPv6 reply: %m", ifp->name);
+ return;
+ }
+
+ free(state->old);
+ state->old = state->new;
+ state->old_len = state->new_len;
+ state->new = malloc(len);
+ state->new = m;
+ memcpy(m, r, len);
+ state->new_len = len;
+
+ op = dhcp6_get_op(r->type);
+ if (r->type != DHCP6_REPLY) {
+ syslog(LOG_ERR, "%s: invalid DHCP6 type %s (%d)",
+ ifp->name, op, r->type);
+ return;
+ }
+
+ syslog(LOG_INFO, "%s: %s received from %s", ifp->name, op, sfrom);
+ switch(state->state) {
+ case DH6S_INFORM:
+ reason = "INFORM6";
+ break;
+ default:
+ reason = "UNKNOWN6";
+ break;
+ }
+ run_script_reason(ifp, options & DHCPCD_TEST ? "TEST" : reason);
+ if (options & DHCPCD_TEST ||
+ (ifp->state->options->options & DHCPCD_INFORM &&
+ !(options & DHCPCD_MASTER)))
+ {
+#ifdef DEBUG_MEMORY
+ dhcp6_free(ifp);
+#endif
+ exit(EXIT_SUCCESS);
+ }
+ delete_timeout(NULL, ifp);
+}
+
+
+static int
+dhcp6_open(void)
+{
+ struct sockaddr_in6 sa;
+ int n;
+
+ if (sndbuf == NULL && dhcp6_init() == -1)
+ return -1;
+
+ memset(&sa, 0, sizeof(sa));
+ sa.sin6_family = AF_INET6;
+ sa.sin6_port = htons(DHCP6_CLIENT_PORT);
+#ifdef BSD
+ sa.sin6_len = sizeof(sa);
+#endif
+
+ sock = socket(PF_INET6, SOCK_DGRAM, IPPROTO_UDP);
+ if (sock == -1)
+ return -1;
+
+ n = 1;
+ if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR,
+ &n, sizeof(n)) == -1)
+ goto errexit;
+
+ n = 1;
+ if (setsockopt(sock, SOL_SOCKET, SO_BROADCAST,
+ &n, sizeof(n)) == -1)
+ goto errexit;
+
+#ifdef SO_REUSEPORT
+ n = 1;
+ if (setsockopt(sock, SOL_SOCKET, SO_REUSEPORT,
+ &n, sizeof(n)) == -1)
+ goto errexit;
+#endif
+
+ if (bind(sock, (struct sockaddr *)&sa, sizeof(sa)) == -1)
+ goto errexit;
+
+ n = 1;
+ if (setsockopt(sock, IPPROTO_IPV6, IPV6_RECVPKTINFO,
+ &n, sizeof(n)) == -1)
+ goto errexit;
+
+ if (set_cloexec(sock) == -1 || set_nonblock(sock) == -1)
+ goto errexit;
+
+ add_event(sock, dhcp6_handledata, NULL);
+
+ return 0;
+
+errexit:
+ close(sock);
+ return -1;
+}
+
+int
+dhcp6_start(struct interface *ifp, int manage)
+{
+ struct dhcp6_state *state;
+
+ state = D6_STATE(ifp);
+ if (state) {
+ /* We're already running DHCP6 */
+ /* XXX: What if the managed flag changes? */
+ return 0;
+ }
+
+ syslog(LOG_INFO, "%s: %s", ifp->name,
+ manage ? "soliciting DHCPv6 address" :
+ "requesting DHCPv6 information");
+
+ if (sock == -1 && dhcp6_open() == -1)
+ return -1;
+
+ if (duid == NULL) {
+ duid = malloc(DUID_LEN);
+ if (duid == NULL)
+ return -1;
+ duid_len = get_duid(duid, ifp);
+ }
+
+ ifp->if_data[IF_DATA_DHCP6] = calloc(1, sizeof(*state));
+ state = D6_STATE(ifp);
+ if (state == NULL)
+ return -1;
+
+ state->state = manage ? DH6S_INIT : DH6S_INFORM;
+ state->start_uptime = uptime();
+
+ if (dhcp6_makemessage(ifp) == -1)
+ return -1;
+
+ if (state->state == DH6S_INFORM)
+ dhcp6_sendinform(ifp);
+
+ return 1;
+}
+
+static void
+dhcp6_freedrop(struct interface *ifp, int drop)
+{
+ struct dhcp6_state *state;
+
+ delete_timeout(NULL, ifp);
+ state = D6_STATE(ifp);
+ if (state) {
+ if (drop && state->new)
+ run_script_reason(ifp, "STOP6");
+ free(state->send);
+ free(state->new);
+ free(state->old);
+ free(state);
+ ifp->if_data[IF_DATA_DHCP6] = NULL;
+ }
+
+ /* If we don't have any more DHCP6 enabled interfaces,
+ * close the global socket */
+ for (ifp = ifaces; ifp; ifp = ifp->next)
+ if (D6_STATE(ifp))
+ break;
+ if (ifp == NULL && sock != -1) {
+ close(sock);
+ delete_event(sock);
+ sock = -1;
+ }
+}
+
+void
+dhcp6_drop(struct interface *ifp)
+{
+
+ dhcp6_freedrop(ifp, 1);
+}
+
+void
+dhcp6_free(struct interface *ifp)
+{
+
+ dhcp6_freedrop(ifp, 0);
+}
+
+ssize_t
+dhcp6_env(char **env, const char *prefix, const struct interface *ifp,
+ const struct dhcp6_message *m, ssize_t mlen)
+{
+ const struct if_options *ifo;
+ const struct dhcp_opt *opt;
+ const struct dhcp6_option *o;
+ ssize_t len, e;
+ uint16_t ol;
+ const uint8_t *od;
+ char **ep, *v, *val;
+
+ e = 0;
+ ep = env;
+ ifo = ifp->state->options;
+ for (opt = dhcp6_opts; opt->option; opt++) {
+ if (!opt->var)
+ continue;
+ if (has_option_mask(ifo->nomask6, opt->option))
+ continue;
+ o = dhcp6_getoption(opt->option, m, mlen);
+ if (o == NULL)
+ continue;
+ if (env == NULL) {
+ e++;
+ continue;
+ }
+ ol = ntohs(o->len);
+ od = D6_COPTION_DATA(o);
+ len = print_option(NULL, 0, opt->type, ol, od);
+ if (len < 0)
+ return -1;
+ e = strlen(prefix) + 6 + strlen(opt->var) + len + 4;
+ v = val = *ep++ = xmalloc(e);
+ v += snprintf(val, e, "%s_dhcp6_%s=", prefix, opt->var);
+ if (len != 0)
+ print_option(v, len, opt->type, ol, od);
+
+ }
+
+ if (env == NULL)
+ return e;
+ return ep - env;
+}
--- /dev/null
+/*
+ * dhcpcd - DHCP client daemon
+ * Copyright (c) 2006-2012 Roy Marples <roy@marples.name>
+ * All rights reserved
+
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef DHCP6_H
+#define DHCP6_H
+
+#include "dhcpcd.h"
+
+/* UDP port numbers for DHCP */
+#define DHCP6_CLIENT_PORT 546
+#define DHCP6_SERVER_PORT 547
+
+/* DHCP message type */
+#define DHCP6_SOLICIT 1
+#define DHCP6_ADVERTISE 2
+#define DHCP6_REQUEST 3
+#define DHCP6_CONFIRM 4
+#define DHCP6_RENEW 5
+#define DHCP6_REBIND 6
+#define DHCP6_REPLY 7
+#define DHCP6_RELEASE 8
+#define DHCP6_DECLINE 9
+#define DHCP6_RECONFIGURE 10
+#define DHCP6_INFORMATION_REQ 11
+#define DHCP6_RELAY_FLOW 12
+#define DHCP6_RELAY_REPL 13
+
+#define D6_OPTION_CLIENTID 1
+#define D6_OPTION_SERVERID 2
+#define D6_OPTION_ORO 6
+#define D6_OPTION_IA_ADDR 5
+#define D6_OPTION_PREFERENCE 7
+#define D6_OPTION_ELAPSED 8
+#define D6_OPTION_RAPID_COMMIT 9
+#define D6_OPTION_UNICAST 12
+#define D6_OPTION_STATUS_CODE 13
+#define D6_OPTION_VENDOR 16
+#define D6_OPTION_SIP_SERVERS_NAME 21
+#define D6_OPTION_SIP_SERVERS_ADDRESS 22
+#define D6_OPTION_DNS_SERVERS 23
+#define D6_OPTION_DOMAIN_LIST 24
+#define D6_OPTION_NIS_SERVERS 27
+#define D6_OPTION_NISP_SERVERS 28
+#define D6_OPTION_NIS_DOMAIN_NAME 29
+#define D6_OPTION_NISP_DOMAIN_NAME 30
+#define D6_OPTION_SNTP_SERVERS 31
+#define D6_OPTION_INFO_REFRESH_TIME 32
+#define D6_OPTION_BCMS_SERVER_D 33
+#define D6_OPTION_BCMS_SERVER_A 34
+
+#include "dhcp.h"
+extern const struct dhcp_opt const dhcp6_opts[];
+
+struct dhcp6_message {
+ uint8_t type;
+ uint8_t xid[3];
+ /* followed by options */
+} _packed;
+
+struct dhcp6_option {
+ uint16_t code;
+ uint16_t len;
+ /* followed by data */
+} _packed;
+
+enum DH6S {
+ DH6S_INIT,
+ DH6S_DISCOVER,
+ DH6S_REQUEST,
+ DH6S_BOUND,
+ DH6S_RENEW,
+ DH6S_REBIND,
+ DH6S_REBOOT,
+ DH6S_INFORM,
+ DH6S_RENEW_REQUESTED,
+ DH6S_PROBE
+};
+
+struct dhcp6_state {
+ enum DH6S state;
+ time_t start_uptime;
+ int interval;
+ struct dhcp6_message *send;
+ size_t send_len;
+ struct dhcp6_message *new;
+ size_t new_len;
+ struct dhcp6_message *old;
+ size_t old_len;
+};
+
+#define D6_STATE(ifp) ((struct dhcp6_state *)(ifp)->if_data[IF_DATA_DHCP6])
+#define D6_FIRST_OPTION(m) \
+ ((struct dhcp6_option *) \
+ ((uint8_t *)(m) + sizeof(struct dhcp6_message)))
+#define D6_NEXT_OPTION(o) \
+ ((struct dhcp6_option *) \
+ (((uint8_t *)o) + sizeof(struct dhcp6_option) + ntohs((o)->len)))
+#define D6_OPTION_DATA(o) \
+ ((uint8_t *)(o) + sizeof(struct dhcp6_option))
+#define D6_CFIRST_OPTION(m) \
+ ((const struct dhcp6_option *) \
+ ((const uint8_t *)(m) + sizeof(struct dhcp6_message)))
+#define D6_CNEXT_OPTION(o) \
+ ((const struct dhcp6_option *) \
+ (((const uint8_t *)o) + sizeof(struct dhcp6_option) + ntohs((o)->len)))
+#define D6_COPTION_DATA(o) \
+ ((const uint8_t *)(o) + sizeof(struct dhcp6_option))
+
+void dhcp6_printoptions(void);
+int dhcp6_start(struct interface *, int);
+ssize_t dhcp6_env(char **, const char *, const struct interface *,
+ const struct dhcp6_message *, ssize_t);
+void dhcp6_free(struct interface *);
+void dhcp6_drop(struct interface *);
+
+#endif
fi
}
+# For ease of use, map DHCP6 names onto our DHCP4 names
+case "$reason" in
+INFORM6)
+ new_domain_name_servers="$new_dhcp6_name_servers"
+ new_domain_search="$new_dhcp6_domain_search"
+ ;;
+esac
+
if $if_up || [ "$reason" = ROUTERADVERT ]; then
add_resolv_conf
elif $if_down; then
.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
.\" SUCH DAMAGE.
.\"
-.Dd March 19, 2012
+.Dd October 11, 2012
.Dt DHCPCD-RUN-HOOKS 8
.Os
.Sh NAME
.It Dv CARRIER
dhcpcd has detected the carrier is up.
This is generally just a notification and no action need be taken.
-.It Dv INFORM
+.It Dv INFORM | Dv INFORM6
dhcpcd informed a DHCP server about it's address and obtained other
configuration details.
.It Dv BOUND
This normally happens when dhcpcd does not support the raw interface, which
means it cannot work as a DHCP or ZeroConf client.
Static configuration and DHCP INFORM is still allowed.
-.It Dv STOP
+.It Dv STOP | Dv STOP6
dhcpcd stopped running on the interface.
.It Dv DUMP
dhcpcd has been asked to dump the last lease for the interface.
# dhcpcd client configuration script
# Handy variables and functions for our hooks to use
-if [ "$reason" = ROUTERADVERT ]; then
- ifsuffix=":ra"
-else
- ifsuffix=
-fi
+case "$reason" in
+ ROUTERADVERT)
+ ifsuffix=":ra";;
+ INFORM6|BOUND6|RENEW6|REBIND6|EXPIRE6|RELEASE6|STOP6)
+ ifsuffix=":dhcp6";;
+ *)
+ ifsuffix=;;
+esac
ifname="$interface$ifsuffix"
from=from
.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
.\" SUCH DAMAGE.
.\"
-.Dd June 7, 2012
+.Dd October 11, 2012
.Dt DHCPCD 8
.Os
.Sh NAME
.Nm dhcpcd
-.Nd an RFC 2131 compliant DHCP client
+.Nd a DHCP client
.Sh SYNOPSIS
.Nm
-.Op Fl ABbDdEGgHJKkLnpqTVw
+.Op Fl 46ABbDdEGgHJKkLnpqTVw
.Op Fl C , Fl Fl nohook Ar hook
.Op Fl c , Fl Fl script Ar script
.Op Fl e , Fl Fl env Ar value
.Nm
sends Neighbor Solicitions to each advertising router periodically and will
expire the ones that do not respond.
+.Pp
+.Nm
+is also an implemenation of a DHCPv6 client as specified in
+.Li RFC 3315
+but only for sending Information Request messages when instructed to do so
+by an IPv6 Router Advertisment or manually.
.Ss Local Link configuration
If
.Nm
configured exactly how the the DHCP server wants.
Here are some options that deal with turning these bits off.
.Bl -tag -width indent
+.It Fl 4 , Fl Fl ipv4only
+Only configure IPv4.
+.It Fl 6 , Fl Fl ipv6only
+Only confgiure IPv6.
.It Fl A , Fl Fl noarp
Don't request or claim the address by ARP.
This also disables IPv4LL.
.Xr dhcpcd-run-hooks 8 ,
.Xr resolvconf 8
.Sh STANDARDS
-RFC 951, RFC 1534, RFC 2131, RFC 2132, RFC 2855, RFC 3004, RFC 3361, RFC 3396,
-RFC 3397, RFC 3442, RFC 3927, RFC 4361, RFC 4390, RFC 4702, RFC 4861, RFC 5969,
-RFC 6106.
+RFC 951, RFC 1534, RFC 2131, RFC 2132, RFC 2855, RFC 3004, RFC 3315,RFC 3361,
+RFC 3396, RFC 3397, RFC 3442, RFC 3927, RFC 4361, RFC 4390, RFC 4702, RFC 4861,
+RFC 5969, RFC 6106.
.Sh AUTHORS
.An Roy Marples Aq roy@marples.name
.Sh BUGS
#include "configure.h"
#include "control.h"
#include "dhcpcd.h"
+#include "dhcp6.h"
#include "duid.h"
#include "eloop.h"
#include "if-options.h"
usage(void)
{
-printf("usage: "PACKAGE"\t[-ABbDdEGgHJKkLnpqTVw]\n"
+printf("usage: "PACKAGE"\t[-46ABbDdEGgHJKkLnpqTVw]\n"
"\t\t[-C, --nohook hook] [-c, --script script]\n"
"\t\t[-e, --env value] [-F, --fqdn FQDN] [-f, --config file]\n"
"\t\t[-h, --hostname hostname] [-I, --clientid clientid]\n"
else
ifaces = ifp->next;
+ dhcp6_drop(iface);
ipv6rs_drop(iface);
if (strcmp(iface->state->reason, "RELEASE") != 0)
drop_dhcp(iface, "STOP");
free(iface->clientid);
iface->clientid = NULL;
+ if (!(ifo->options & DHCPCD_IPV4))
+ return;
+
if (*ifo->clientid) {
iface->clientid = xmalloc(ifo->clientid[0] + 1);
memcpy(iface->clientid, ifo->clientid, ifo->clientid[0] + 1);
free(iface->state->offer);
iface->state->offer = NULL;
- if (options & DHCPCD_IPV6RS && ifo->options & DHCPCD_IPV6RS)
+ if (options & DHCPCD_IPV6RS && ifo->options & DHCPCD_IPV6RS &&
+ !(ifo->options & DHCPCD_INFORM))
ipv6rs_start(iface);
if (iface->state->arping_index < ifo->arping_len) {
start_static(iface);
return;
}
+
+ if (ifo->options & DHCPCD_INFORM && ifo->options & DHCPCD_IPV6)
+ dhcp6_start(iface, 0);
+
+ if (!(ifo->options & DHCPCD_IPV4))
+ return;
+
if (ifo->options & DHCPCD_INFORM) {
start_inform(iface);
return;
main(int argc, char **argv)
{
struct interface *iface;
- int opt, oi = 0, signal_fd, sig = 0, i, control_fd;
+ int opt, oi = 0, signal_fd, sig = 0, i, control_fd, family = 0;
size_t len;
pid_t pid;
struct timespec ts;
while ((opt = getopt_long(argc, argv, IF_OPTS, cf_options, &oi)) != -1)
{
switch (opt) {
+ case '4':
+ family = AF_INET;
+ break;
+ case '6':
+ family = AF_INET6;
+ break;
case 'f':
cffile = optarg;
break;
i = 2;
break;
case 'V':
- print_options();
+ printf("Interface options:\n");
+ if_printoptions();
+ if (family == 0 || family == AF_INET) {
+ printf("\nDHCPv4 options:\n");
+ print_options();
+ }
+ if (family == 0 || family == AF_INET6) {
+ printf("\nDHCPv6 options:\n");
+ dhcp6_printoptions();
+ }
exit(EXIT_SUCCESS);
case '?':
usage();
.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
.\" SUCH DAMAGE.
.\"
-.Dd May 21, 2012
+.Dd October 11, 2012
.Dt DHCPCD.CONF 5 SMM
.Os
.Sh NAME
If
.Ar hostname
is a FQDN (ie, contains a .) then it will be encoded as such.
+.It Ic ipv4only
+Only configure IPv4.
+.It Ic ipv6only
+Only confgiure IPv6.
.It Ic fqdn Op none | ptr | both
none disables FQDN encoding, ptr just asks the DHCP server to update the PTR
record of the host in DNS whereas both also updates the A record.
Set this option so to make
.Nm dhcpcd
always fork on an RA.
-.It ic ipv6ra_own
+.It Ic ipv6ra_own
Disables kernel IPv6 Router Advertisment processing so dhcpcd can manage
addresses and routes.
-.It ic ipv6ra_own_default
+.It Ic ipv6ra_own_default
Each time dhcpcd receives an IPv6 Router Adveristment, dhcpcd will manage
the default route only.
This allows dhcpcd to prefer an interface for outbound traffic based on metric
and/or user selection rather than the kernel.
-.It ic ipv6rs
+.It Ic ipv6rs
Enables IPv6 Router Advertisment solicitation.
This is on by default, but is documented here in the case where it is disabled
globally but needs to be enabled for one interface.
#include "config.h"
#include "common.h"
+#include "dhcp.h"
+#include "dhcp6.h"
#include "if-options.h"
#include "net.h"
#include "platform.h"
{"ipv6ra_fork", no_argument, NULL, O_IPV6RA_FORK},
{"ipv6ra_own", no_argument, NULL, O_IPV6RA_OWN},
{"ipv6ra_own_default", no_argument, NULL, O_IPV6RA_OWN_D},
+ {"ipv4only", no_argument, NULL, '4'},
+ {"ipv6only", no_argument, NULL, '6'},
{NULL, 0, NULL, '\0'}
};
return 0;
}
+static const char *
+set_option_space(const char *arg, const struct dhcp_opt **d,
+ struct if_options *ifo,
+ uint8_t *request[], uint8_t *require[], uint8_t *no[])
+{
+
+ if (strncmp(arg, "dhcp6_", strlen("dhcp6_")) == 0) {
+ *d = dhcp6_opts;
+ *request = ifo->requestmask6;
+ *require = ifo->requiremask6;
+ *no = ifo->nomask6;
+ return arg + strlen("dhcp6_");
+ }
+ *d = dhcp_opts;
+ *request = ifo->requestmask;
+ *require = ifo->requiremask;
+ *no = ifo->nomask;
+ return arg;
+}
+
static int
parse_option(struct if_options *ifo, int opt, const char *arg)
{
ssize_t s;
struct in_addr addr, addr2;
struct rt *rt;
+ const struct dhcp_opt const *d;
+ uint8_t *request, *require, *no;
switch(opt) {
case 'f': /* FALLTHROUGH */
}
break;
case 'o':
- if (make_option_mask(ifo->requestmask, arg, 1) != 0) {
+ arg = set_option_space(arg, &d, ifo, &request, &require, &no);
+ if (make_option_mask(d, request, arg, 1) != 0) {
syslog(LOG_ERR, "unknown option `%s'", arg);
return -1;
}
ifo->req_mask.s_addr = 0;
break;
case 's':
+ if (ifo->options & DHCPCD_IPV6 &&
+ !(ifo->options & DHCPCD_IPV4))
+ {
+ ifo->options |= DHCPCD_INFORM;
+ break;
+ }
if (arg && *arg != '\0') {
if (parse_addr(&ifo->req_addr, &ifo->req_mask,
arg) != 0)
ifo->options &= ~DHCPCD_IPV4LL;
break;
case 'O':
- if (make_option_mask(ifo->requestmask, arg, -1) != 0 ||
- make_option_mask(ifo->requiremask, arg, -1) != 0 ||
- make_option_mask(ifo->nomask, arg, 1) != 0)
+ arg = set_option_space(arg, &d, ifo, &request, &require, &no);
+ if (make_option_mask(d, request, arg, -1) != 0 ||
+ make_option_mask(d, require, arg, -1) != 0 ||
+ make_option_mask(d, no, arg, 1) != 0)
{
syslog(LOG_ERR, "unknown option `%s'", arg);
return -1;
}
break;
case 'Q':
- if (make_option_mask(ifo->requiremask, arg, 1) != 0 ||
- make_option_mask(ifo->requestmask, arg, 1) != 0)
+ arg = set_option_space(arg, &d, ifo, &request, &require, &no);
+ if (make_option_mask(d, require, arg, 1) != 0 ||
+ make_option_mask(d, request, arg, 1) != 0)
{
syslog(LOG_ERR, "unknown option `%s'", arg);
return -1;
case 'Z':
ifdv = splitv(&ifdc, ifdv, arg);
break;
+ case '4':
+ ifo->options &= ~(DHCPCD_IPV6 | DHCPCD_IPV6RS);
+ ifo->options |= DHCPCD_IPV4;
+ break;
+ case '6':
+ ifo->options &= ~DHCPCD_IPV4;
+ ifo->options |= DHCPCD_IPV6 | DHCPCD_IPV6RS;
+ break;
case O_ARPING:
if (parse_addr(&addr, NULL, arg) != 0)
return -1;
ifo->arping[ifo->arping_len++] = addr.s_addr;
break;
case O_DESTINATION:
- if (make_option_mask(ifo->dstmask, arg, 2) != 0) {
+ if (make_option_mask(dhcp_opts, ifo->dstmask, arg, 2) != 0) {
if (errno == EINVAL)
syslog(LOG_ERR, "option `%s' does not take"
" an IPv4 address", arg);
/* Seed our default options */
ifo = xzalloc(sizeof(*ifo));
+ ifo->options |= DHCPCD_IPV4 | DHCPCD_IPV4LL;
ifo->options |= DHCPCD_GATEWAY | DHCPCD_DAEMONISE | DHCPCD_LINK;
- ifo->options |= DHCPCD_ARP | DHCPCD_IPV4LL;
- ifo->options |= DHCPCD_IPV6RS | DHCPCD_IPV6RA_REQRDNSS;
+ ifo->options |= DHCPCD_ARP;
+ ifo->options |= DHCPCD_IPV6 | DHCPCD_IPV6RS | DHCPCD_IPV6RA_REQRDNSS;
ifo->timeout = DEFAULT_TIMEOUT;
ifo->reboot = DEFAULT_REBOOT;
ifo->metric = -1;
#include <getopt.h>
#include <limits.h>
+#include <stdint.h>
/* Don't set any optional arguments here so we retain POSIX
* compatibility with getopt */
-#define IF_OPTS "bc:de:f:gh:i:kl:m:no:pqr:s:t:u:v:wxy:z:ABC:DEF:GHI:JKLO:Q:S:TUVW:X:Z:"
+#define IF_OPTS "46bc:de:f:gh:i:kl:m:no:pqr:s:t:u:v:wxy:z:ABC:DEF:GHI:JKLO:Q:S:TUVW:X:Z:"
#define DEFAULT_TIMEOUT 30
#define DEFAULT_REBOOT 5
#define DHCPCD_IPV6RA_OWN_DEFAULT (1ULL << 34)
#define DHCPCD_IPV4 (1ULL << 35)
#define DHCPCD_FORKED (1ULL << 36)
+#define DHCPCD_IPV6 (1ULL << 37)
extern const struct option cf_options[];
uint8_t requestmask[256 / 8];
uint8_t requiremask[256 / 8];
uint8_t nomask[256 / 8];
+ uint8_t requestmask6[(UINT16_MAX + 1) / 8];
+ uint8_t requiremask6[(UINT16_MAX + 1) / 8];
+ uint8_t nomask6[(UINT16_MAX + 1) / 8];
uint8_t dstmask[256 / 8];
uint32_t leasetime;
time_t timeout;
#include <netinet/ip6.h>
#include <netinet/icmp6.h>
+#ifdef __linux__
+# define _LINUX_IN6_H
+# include <linux/ipv6.h>
+#endif
+
#include <errno.h>
#include <stddef.h>
#include <stdlib.h>
#include <string.h>
#include <syslog.h>
-#ifdef __linux__
-# define _LINUX_IN6_H
-# include <linux/ipv6.h>
-#endif
-
#define ELOOP_QUEUE 1
#include "bind.h"
#include "common.h"
#include "configure.h"
#include "dhcpcd.h"
+#include "dhcp6.h"
#include "eloop.h"
#include "ipv6.h"
#include "ipv6ns.h"
#include "ipv6rs.h"
+/* Debugging Router Solicitations is a lot of spam, so disable it */
+//#define DEBUG_RS
+
#define RTR_SOLICITATION_INTERVAL 4 /* seconds */
#define MAX_RTR_SOLICITATIONS 3 /* times */
struct nd_opt_hdr *ndo;
struct ra_opt *rao;
struct ipv6_addr *ap;
- char *opt;
+ char *opt, *tmp;
struct timeval expire;
- int has_dns, new_rap;
+ int has_dns, new_rap, new_data;
len = recvmsg(sock, &rcvhdr, 0);
if (len == -1) {
if (ifp->index == (unsigned int)pkt.ipi6_ifindex)
break;
if (ifp == NULL) {
- syslog(LOG_ERR, "RA for unexpected interface from %s", sfrom);
+#ifdef DEBUG_RS
+ syslog(LOG_DEBUG, "RA for unexpected interface from %s", sfrom);
+#endif
return;
}
TAILQ_FOREACH(rap, &ipv6_routers, next) {
rap->ns = NULL;
rap->nslen = 0;
}
+ new_data = 1;
syslog(LOG_INFO, "%s: Router Advertisement from %s",
ifp->name, sfrom);
- }
+ } else
+ new_data = 0;
if (rap == NULL) {
rap = xzalloc(sizeof(*rap));
ifp->name);
} else {
opt = xmalloc(l);
- decode_rfc3397(opt, l, n, op);
+ tmp = xmalloc(l);
+ decode_rfc3397(tmp, l, n, op);
+ l = print_string(opt, l, l - 1, (uint8_t *)tmp);
+ free(tmp);
}
break;
}
else if (ipv6_remove_subnet(rap, ap) == -1)
syslog(LOG_ERR, "ipv6_remove_subnet %m");
else
- syslog(ap->new ? LOG_INFO : LOG_DEBUG,
+ syslog(LOG_DEBUG,
"%s: vltime %d seconds, pltime %d seconds",
ifp->name, ap->prefix_vltime,
ap->prefix_pltime);
ipv6_build_routes();
run_script_reason(ifp, options & DHCPCD_TEST ? "TEST" : "ROUTERADVERT");
if (options & DHCPCD_TEST)
- exit(EXIT_SUCCESS);
+ goto handle_flag;
/* If we don't require RDNSS then set has_dns = 1 so we fork */
if (!(ifp->state->options->options & DHCPCD_IPV6RA_REQRDNSS))
ipv6rs_expire(ifp);
if (has_dns)
daemonise();
- else if (options & DHCPCD_DAEMONISE && !(options & DHCPCD_DAEMONISED))
+ else if (options & DHCPCD_DAEMONISE &&
+ !(options & DHCPCD_DAEMONISED) && new_data)
syslog(LOG_WARNING,
"%s: did not fork due to an absent RDNSS option in the RA",
ifp->name);
rap->nsprobes = 0;
ipv6ns_sendprobe(rap);
}
+
+handle_flag:
+ if (rap->flags & ND_RA_FLAG_MANAGED) {
+ if (new_data)
+ syslog(LOG_WARNING, "%s: no support for DHCPv6 management",
+ ifp->name);
+// if (dhcp6_start(ifp, 1) == -1)
+// syslog(LOG_ERR, "dhcp6_start: %s: %m", ifp->name);
+ } else if (rap->flags & ND_RA_FLAG_OTHER) {
+ if (dhcp6_start(ifp, 0) == -1)
+ syslog(LOG_ERR, "dhcp6_start: %s: %m", ifp->name);
+ } else {
+ if (new_data)
+ syslog(LOG_DEBUG, "%s: No DHCPv6 instruction in RA",
+ ifp->name);
+ }
}
int
#include "config.h"
#include "common.h"
#include "dhcp.h"
+#include "dhcp6.h"
#include "if-options.h"
#include "ipv6rs.h"
#include "net.h"
{
if (!iface)
return;
+ dhcp6_free(iface);
ipv6rs_free(iface);
if (iface->state) {
free_options(iface->state->options);