mirror of
https://github.com/rsmarples/dhcpcd.git
synced 2024-11-23 18:14:09 +08:00
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:
parent
27805e96f6
commit
d7555c1259
2
Makefile
2
Makefile
@ -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
|
||||
|
60
configure.c
60
configure.c
@ -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) {
|
||||
|
@ -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
2
defs.h
@ -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
84
dhcp.c
@ -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
33
dhcp.h
@ -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
821
dhcp6.c
Normal 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
140
dhcp6.h
Normal 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
|
@ -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
|
||||
|
@ -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.
|
||||
|
@ -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
|
||||
|
22
dhcpcd.8.in
22
dhcpcd.8.in
@ -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
|
||||
|
36
dhcpcd.c
36
dhcpcd.c
@ -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();
|
||||
|
@ -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.
|
||||
|
62
if-options.c
62
if-options.c
@ -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;
|
||||
|
@ -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;
|
||||
|
54
ipv6rs.c
54
ipv6rs.c
@ -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
2
net.c
@ -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);
|
||||
|
Loading…
Reference in New Issue
Block a user