Add DHCPv6 INFORM support.

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.
This commit is contained in:
Roy Marples 2012-10-12 10:31:51 +00:00
parent 27805e96f6
commit d7555c1259
18 changed files with 1266 additions and 99 deletions

View File

@ -3,7 +3,7 @@
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

View File

@ -44,6 +44,7 @@
#include "common.h"
#include "configure.h"
#include "dhcp.h"
#include "dhcp6.h"
#include "if-options.h"
#include "if-pref.h"
#include "ipv6rs.h"
@ -54,6 +55,28 @@
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)
{
@ -159,15 +182,21 @@ make_env(const struct interface *iface, const char *reason, char ***argv)
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;
@ -219,7 +248,10 @@ make_env(const struct interface *iface, const char *reason, char ***argv)
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 {
@ -258,6 +290,15 @@ make_env(const struct interface *iface, const char *reason, char ***argv)
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) {
@ -270,6 +311,15 @@ dumplease:
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) {

View File

@ -30,6 +30,7 @@
#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);

2
defs.h
View File

@ -28,7 +28,7 @@
#define CONFIG_H
#define PACKAGE "dhcpcd"
#define VERSION "5.6.2"
#define VERSION "5.99.1"
#ifndef CONFIG
# define CONFIG SYSCONFDIR "/" PACKAGE ".conf"

84
dhcp.c
View File

@ -37,23 +37,6 @@
#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.
@ -61,13 +44,7 @@
* 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,
@ -165,23 +142,10 @@ static const struct dhcp_opt const dhcp_opts[] = {
{ 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
@ -193,9 +157,6 @@ print_options(void)
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);
@ -204,7 +165,8 @@ print_options(void)
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;
@ -214,7 +176,7 @@ int make_option_mask(uint8_t *mask, const char *opts, int add)
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;
@ -1177,7 +1139,7 @@ read_lease(const struct interface *iface)
return dhcp;
}
static ssize_t
ssize_t
print_string(char *s, ssize_t len, int dl, const uint8_t *data)
{
uint8_t c;
@ -1243,7 +1205,7 @@ print_string(char *s, ssize_t len, int dl, const uint8_t *data)
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;
@ -1254,7 +1216,8 @@ print_option(char *s, ssize_t len, int type, int dl, const uint8_t *data)
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);
@ -1289,6 +1252,22 @@ print_option(char *s, ssize_t len, int type, int dl, const uint8_t *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;
@ -1307,6 +1286,11 @@ print_option(char *s, ssize_t len, int type, int dl, const uint8_t *data)
} 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;
@ -1317,7 +1301,7 @@ print_option(char *s, ssize_t len, int type, int dl, const uint8_t *data)
t = data;
e = data + dl;
while (data < e) {
if (data != t) {
if (data != t && type != BINHEX) {
*s++ = ' ';
bytes++;
len--;
@ -1349,6 +1333,14 @@ print_option(char *s, ssize_t len, int type, int dl, const uint8_t *data)
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;

33
dhcp.h
View File

@ -114,6 +114,26 @@ enum DHO {
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
@ -173,10 +193,18 @@ struct dhcp_lease {
#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);
@ -189,6 +217,9 @@ int get_option_uint8(uint8_t *, 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 *);

821
dhcp6.c Normal file
View File

@ -0,0 +1,821 @@
/*
* 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;
}

140
dhcp6.h Normal file
View File

@ -0,0 +1,140 @@
/*
* 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

View File

@ -138,6 +138,14 @@ remove_resolv_conf()
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

View File

@ -22,7 +22,7 @@
.\" 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
@ -75,7 +75,7 @@ dhcpcd is starting up and any pre-initialisation should be done.
.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
@ -110,7 +110,7 @@ dhcpcd failed to operate on the interface.
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.

View File

@ -2,11 +2,14 @@
# 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

View File

@ -22,15 +22,15 @@
.\" 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
@ -114,6 +114,12 @@ is managing routes,
.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
@ -424,6 +430,10 @@ However, there are sometimes situations where you don't want the things to be
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.
@ -591,9 +601,9 @@ running on the
.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

View File

@ -63,6 +63,7 @@ const char copyright[] = "Copyright (c) 2006-2012 Roy Marples";
#include "configure.h"
#include "control.h"
#include "dhcpcd.h"
#include "dhcp6.h"
#include "duid.h"
#include "eloop.h"
#include "if-options.h"
@ -150,7 +151,7 @@ static void
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"
@ -269,6 +270,7 @@ stop_interface(struct interface *iface)
else
ifaces = ifp->next;
dhcp6_drop(iface);
ipv6rs_drop(iface);
if (strcmp(iface->state->reason, "RELEASE") != 0)
drop_dhcp(iface, "STOP");
@ -818,6 +820,9 @@ configure_interface1(struct interface *iface)
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);
@ -1185,7 +1190,8 @@ start_interface(void *arg)
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) {
@ -1196,6 +1202,13 @@ start_interface(void *arg)
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;
@ -1771,7 +1784,7 @@ int
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;
@ -1807,6 +1820,12 @@ main(int argc, char **argv)
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;
@ -1829,7 +1848,16 @@ main(int argc, char **argv)
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();

View File

@ -22,7 +22,7 @@
.\" 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
@ -133,6 +133,10 @@ is an empty string then the current system hostname is sent.
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.
@ -154,15 +158,15 @@ RDNSS option.
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.

View File

@ -42,6 +42,8 @@
#include "config.h"
#include "common.h"
#include "dhcp.h"
#include "dhcp6.h"
#include "if-options.h"
#include "net.h"
#include "platform.h"
@ -114,6 +116,8 @@ const struct option cf_options[] = {
{"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'}
};
@ -334,6 +338,26 @@ parse_addr(struct in_addr *addr, struct in_addr *net, const char *arg)
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)
{
@ -342,6 +366,8 @@ 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 */
@ -419,7 +445,8 @@ parse_option(struct if_options *ifo, int opt, const char *arg)
}
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;
}
@ -437,6 +464,12 @@ parse_option(struct if_options *ifo, int opt, const char *arg)
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)
@ -612,17 +645,19 @@ parse_option(struct if_options *ifo, int opt, const char *arg)
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;
@ -730,6 +765,14 @@ parse_option(struct if_options *ifo, int opt, const char *arg)
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;
@ -738,7 +781,7 @@ parse_option(struct if_options *ifo, int opt, const char *arg)
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);
@ -808,9 +851,10 @@ read_config(const char *file,
/* 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;

View File

@ -34,10 +34,11 @@
#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
@ -83,6 +84,7 @@
#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[];
@ -91,6 +93,9 @@ struct if_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;

View File

@ -33,27 +33,31 @@
#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 */
@ -379,9 +383,9 @@ ipv6rs_handledata(_unused void *arg)
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) {
@ -438,7 +442,9 @@ ipv6rs_handledata(_unused void *arg)
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) {
@ -460,9 +466,11 @@ ipv6rs_handledata(_unused void *arg)
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));
@ -642,7 +650,10 @@ ipv6rs_handledata(_unused void *arg)
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;
}
@ -691,7 +702,7 @@ ipv6rs_handledata(_unused void *arg)
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);
@ -701,7 +712,7 @@ ipv6rs_handledata(_unused void *arg)
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))
@ -714,7 +725,8 @@ ipv6rs_handledata(_unused void *arg)
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);
@ -727,6 +739,22 @@ ipv6rs_handledata(_unused void *arg)
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

2
net.c
View File

@ -65,6 +65,7 @@
#include "config.h"
#include "common.h"
#include "dhcp.h"
#include "dhcp6.h"
#include "if-options.h"
#include "ipv6rs.h"
#include "net.h"
@ -187,6 +188,7 @@ free_interface(struct interface *iface)
{
if (!iface)
return;
dhcp6_free(iface);
ipv6rs_free(iface);
if (iface->state) {
free_options(iface->state->options);